I collect a few corner cases and brain teasers and would always like to hear more. The page only really covers C# language bits and bobs, but I also find core .NET things interesting too. For example, here's one which isn't on the page, but which I find incredible:
我收集了一些角落的案例和脑筋急转弯,总是喜欢听到更多。页面只覆盖了c#语言位和bobs,但我也发现了core . net的一些有趣的东西。例如,这里有一个不在页面上,但我觉得难以置信:
string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x, y));
I'd expect that to print False - after all, "new" (with a reference type) always creates a new object, doesn't it? The specs for both C# and the CLI indicate that it should. Well, not in this particular case. It prints True, and has done on every version of the framework I've tested it with. (I haven't tried it on Mono, admittedly...)
我希望打印错误——毕竟,“new”(带有引用类型)总是创建一个新对象,不是吗?c#和CLI的规范都表明应该这样做。不是在这个特例中。它打印的是True,并且在我测试过的每个版本的框架上都完成了。(当然,我还没试过。)
Just to be clear, this is only an example of the kind of thing I'm looking for - I wasn't particularly looking for discussion/explanation of this oddity. (It's not the same as normal string interning; in particular, string interning doesn't normally happen when a constructor is called.) I was really asking for similar odd behaviour.
要说清楚,这只是我要找的东西的一个例子——我并不是特别想要讨论这个古怪的东西。(它与普通的串接不一样;特别是,当调用构造函数时,通常不会发生字符串插入。我真的想要类似的古怪行为。
Any other gems lurking out there?
还有其他的宝石吗?
37 个解决方案
#1
394
I think I showed you this one before, but I like the fun here - this took some debugging to track down! (the original code was obviously more complex and subtle...)
我想我之前给你们看过这个,但是我喜欢这里的乐趣-这需要一些调试来跟踪!(原来的代码显然更加复杂和微妙……)
static void Foo<T>() where T : new()
{
T t = new T();
Console.WriteLine(t.ToString()); // works fine
Console.WriteLine(t.GetHashCode()); // works fine
Console.WriteLine(t.Equals(t)); // works fine
// so it looks like an object and smells like an object...
// but this throws a NullReferenceException...
Console.WriteLine(t.GetType());
}
So what was T...
所以T是什么…
Answer: any Nullable<T>
- such as int?
. All the methods are overridden, except GetType() which can't be; so it is cast (boxed) to object (and hence to null) to call object.GetType()... which calls on null ;-p
答:任何可空的
Update: the plot thickens... Ayende Rahien threw down a similar challenge on his blog, but with a where T : class, new()
:
更新:情节变稠……Ayende Rahien在他的博客上也提出了类似的挑战,但是有一个T: class, new():
private static void Main() {
CanThisHappen<MyFunnyType>();
}
public static void CanThisHappen<T>() where T : class, new() {
var instance = new T(); // new() on a ref-type; should be non-null, then
Debug.Assert(instance != null, "How did we break the CLR?");
}
But it can be defeated! Using the same indirection used by things like remoting; warning - the following is pure evil:
但它可以被打败!使用与remoting相同的间接方法;警告-以下是纯粹的邪恶:
class MyFunnyProxyAttribute : ProxyAttribute {
public override MarshalByRefObject CreateInstance(Type serverType) {
return null;
}
}
[MyFunnyProxy]
class MyFunnyType : ContextBoundObject { }
With this in place, the new()
call is redirected to the proxy (MyFunnyProxyAttribute
), which returns null
. Now go and wash your eyes!
这样,新的()调用被重定向到代理(MyFunnyProxyAttribute),该代理返回null。现在去洗你的眼睛吧!
#2
216
Bankers' Rounding.
银行家的舍入。
This one is not so much a compiler bug or malfunction, but certainly a strange corner case...
这不是一个编译器错误或故障,但肯定是一个奇怪的角落。
The .Net Framework employs a scheme or rounding known as Banker's Rounding.
. net框架采用了一种名为“银行家四舍五入”的方案。
In Bankers' Rounding the 0.5 numbers are rounded to the nearest even number, so
在银行家的四舍五入中,0.5个数字被四舍五入到最近的偶数,所以。
Math.Round(-0.5) == 0
Math.Round(0.5) == 0
Math.Round(1.5) == 2
Math.Round(2.5) == 2
etc...
This can lead to some unexpected bugs in financial calculations based on the more well known Round-Half-Up rounding.
这可能会导致一些意料之外的财务计算错误,这些错误是基于更广为人知的圆半整数的四舍五入。
This is also true of Visual Basic.
这在视觉基础上也是正确的。
#3
176
What will this function do if called as Rec(0)
(not under the debugger)?
如果调用Rec(0)(不在调试器下),该函数会做什么?
static void Rec(int i)
{
Console.WriteLine(i);
if (i < int.MaxValue)
{
Rec(i + 1);
}
}
Answer:
答:
- On 32-bit JIT it should result in a *Exception
- 在32位JIT上,它应该导致*Exception。
- On 64-bit JIT it should print all the numbers to int.MaxValue
- 在64位JIT上,它应该将所有数字打印到int.MaxValue。
This is because the 64-bit JIT compiler applies tail call optimisation, whereas the 32-bit JIT does not.
这是因为64位JIT编译器应用尾部调用优化,而32位JIT编译器则没有。
Unfortunately I haven't got a 64-bit machine to hand to verify this, but the method does meet all the conditions for tail-call optimisation. If anybody does have one I'd be interested to see if it's true.
不幸的是,我还没有一个64位的机器来验证这一点,但是这个方法确实满足了对尾调用优化的所有条件。如果有人有的话,我很想看看是不是真的。
#4
112
Assign This!
This is one that I like to ask at parties (which is probably why I don't get invited anymore):
这是我在派对上喜欢问的问题(这可能是我不再被邀请的原因):
Can you make the following piece of code compile?
你能做下面的代码编译吗?
public void Foo()
{
this = new Teaser();
}
An easy cheat could be:
一个简单的欺骗可以是:
string cheat = @"
public void Foo()
{
this = new Teaser();
}
";
But the real solution is this:
但真正的解决方法是:
public struct Teaser
{
public void Foo()
{
this = new Teaser();
}
}
So it's a little know fact that value types (structs) can reassign their this
variable.
因此,有一点知道,值类型(struct)可以重新分配这个变量。
#5
100
Few years ago, when working on loyality program, we had an issue with the amount of points given to customers. The issue was related to casting/converting double to int.
几年前,在“忠诚计划”(loyality program)工作的时候,我们遇到了一个问题,那就是给客户的分数是多少。这个问题与铸造/转换成整数有关。
In code below:
下面的代码:
double d = 13.6;
int i1 = Convert.ToInt32(d);
int i2 = (int)d;
does i1 == i2 ?
i1 == i2 ?
It turns out that i1 != i2. Because of different rounding policies in Convert and cast operator the actual values are:
结果是i1 != i2。由于转换和转换算子的舍入策略不同,实际值为:
i1 == 14
i2 == 13
It's always better to call Math.Ceiling() or Math.Floor() (or Math.Round with MidpointRounding that meets our requirements)
最好调用Math. ceiling()或Math. floor()(或Math)。以符合我们要求的中点为圆心
int i1 = Convert.ToInt32( Math.Ceiling(d) );
int i2 = (int) Math.Ceiling(d);
#6
74
They should have made 0 an integer even when there's an enum function overload.
即使有枚举函数重载,它们也应该使0为整数。
I knew C# core team rationale for mapping 0 to enum, but still, it is not as orthogonal as it should be. Example from Npgsql.
我知道将0映射到enum的c#核心团队的基本原理,但它仍然不像它应该的那样正交。来自Npgsql示例。
Test example:
测试的例子:
namespace Craft
{
enum Symbol { Alpha = 1, Beta = 2, Gamma = 3, Delta = 4 };
class Mate
{
static void Main(string[] args)
{
JustTest(Symbol.Alpha); // enum
JustTest(0); // why enum
JustTest((int)0); // why still enum
int i = 0;
JustTest(Convert.ToInt32(0)); // have to use Convert.ToInt32 to convince the compiler to make the call site use the object version
JustTest(i); // it's ok from down here and below
JustTest(1);
JustTest("string");
JustTest(Guid.NewGuid());
JustTest(new DataTable());
Console.ReadLine();
}
static void JustTest(Symbol a)
{
Console.WriteLine("Enum");
}
static void JustTest(object o)
{
Console.WriteLine("Object");
}
}
}
#7
67
This is one of the most unusual i've seen so far (aside from the ones here of course!):
这是迄今为止我见过的最不寻常的事情之一(当然,除了这里的那些!)
public class Turtle<T> where T : Turtle<T>
{
}
It lets you declare it but has no real use, since it will always ask you to wrap whatever class you stuff in the center with another Turtle.
它可以让你声明它,但没有实际用途,因为它总是要求你把你在中心的东西和另一只乌龟包在一起。
[joke] I guess it's turtles all the way down... [/joke]
[笑话]我猜是海龟一路下来……[/笑话]
#8
65
Here's one I only found out about recently...
这是我最近才发现的一个…
interface IFoo
{
string Message {get;}
}
...
IFoo obj = new IFoo("abc");
Console.WriteLine(obj.Message);
The above looks crazy at first glance, but is actually legal.No, really (although I've missed out a key part, but it isn't anything hacky like "add a class called IFoo
" or "add a using
alias to point IFoo
at a class").
乍一看,上面看起来很疯狂,但实际上是合法的。不,真的(虽然我遗漏了一个关键部分,但它并不是像“添加一个名为IFoo的类”或“在类中添加一个使用别名来指向IFoo”)。
See if you can figure out why, then: Who says you can’t instantiate an interface?
看看你能否找出原因:谁说你不能实例化一个接口?
#9
56
When is a Boolean neither True nor False?
什么时候一个布尔值既不正确也不假?
Bill discovered that you can hack a boolean so that if A is True and B is True, (A and B) is False.
比尔发现你可以破解一个布尔值,这样如果a为真,B为真,(a和B)为假。
砍布尔值
#10
47
I'm arriving a bit late to the party, but I've got
three
four
five:
我来参加派对有点晚了,但我有3个4个5个:
-
If you poll InvokeRequired on a control that hasn't been loaded/shown, it will say false - and blow up in your face if you try to change it from another thread (the solution is to reference this.Handle in the creator of the control).
如果你在一个没有加载/显示的控件上进行了调用,它会说错误,如果你试图从另一个线程中改变它,那么你的脸就会被炸飞(解决方案是引用这个)。在控制的创建者的句柄。
-
Another one which tripped me up is that given an assembly with:
另一个让我绊倒的是,给了一个装配:
enum MyEnum { Red, Blue, }
if you calculate MyEnum.Red.ToString() in another assembly, and in between times someone has recompiled your enum to:
如果您在另一个程序集中计算MyEnum.Red.ToString(),并且在此期间,有人将您的enum重新编译为:
enum MyEnum { Black, Red, Blue, }
at runtime, you will get "Black".
在运行时,您将得到“黑色”。
-
I had a shared assembly with some handy constants in. My predecessor had left a load of ugly-looking get-only properties, I thought I'd get rid of the clutter and just use public const. I was more than a little surprised when VS compiled them to their values, and not references.
我有一个共享程序集,里面有一些方便的常数。我的前任留下了一大堆丑陋的“get-only”属性,我想我应该摆脱混乱,只使用公共的const。当VS将它们编译成它们的值,而不是引用时,我感到有点惊讶。
-
If you implement a new method of an interface from another assembly, but you rebuild referencing the old version of that assembly, you get a TypeLoadException (no implementation of 'NewMethod'), even though you have implemented it (see here).
如果您从另一个程序集实现一个接口的新方法,但是您重新引用了该程序集的旧版本,那么您就得到了一个TypeLoadException(没有“NewMethod”的实现),即使您已经实现了它(参见这里)。
-
Dictionary<,>: "The order in which the items are returned is undefined". This is horrible, because it can bite you sometimes, but work others, and if you've just blindly assumed that Dictionary is going to play nice ("why shouldn't it? I thought, List does"), you really have to have your nose in it before you finally start to question your assumption.
字典<,>:“条目返回的顺序是未定义的”。这很可怕,因为它有时会咬你,但也会影响别人,如果你只是盲目地认为字典会很好(“为什么?”在你开始质疑你的假设之前,你真的必须要有自己的鼻子。
#11
33
VB.NET, nullables and the ternary operator:
VB。NET、nullables和ternary操作符:
Dim i As Integer? = If(True, Nothing, 5)
This took me some time to debug, since I expected i
to contain Nothing
.
这花了我一些时间调试,因为我希望我什么都不包含。
What does i really contain? 0
.
我到底包含了什么?0。
This is surprising but actually "correct" behavior: Nothing
in VB.NET is not exactly the same as null
in CLR: Nothing
can either mean null
or default(T)
for a value type T
, depending on the context. In the above case, If
infers Integer
as the common type of Nothing
and 5
, so, in this case, Nothing
means 0
.
这很奇怪,但实际上是“正确”的行为:VB中没有。在CLR中,NET并不完全等同于null:对于一个值类型T,任何东西都不能表示null或默认值(T),这取决于上下文。在上面的例子中,如果infers Integer是普通类型的Nothing和5,那么,在这种情况下,什么都不是0。
#12
28
I found a second really strange corner case that beats my first one by a long shot.
我发现了另一个非常奇怪的角落,它比我的第一个要远得多。
String.Equals Method (String, String, StringComparison) is not actually side effect free.
字符串。Equals方法(String, String, StringComparison)实际上并不是免费的。
I was working on a block of code that had this on a line by itself at the top of some function:
我在做一个代码块,它在某个函数的顶部有一个这样的代码:
stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase);
Removing that line lead to a stack overflow somewhere else in the program.
删除这一行会导致程序中其他地方的堆栈溢出。
The code turned out to be installing a handler for what was in essence a BeforeAssemblyLoad event and trying to do
代码原来是安装一个处理程序,其本质上是一个beforeassembly加载事件和尝试做的事情。
if (assemblyfilename.EndsWith("someparticular.dll", StringComparison.InvariantCultureIgnoreCase))
{
assemblyfilename = "someparticular_modified.dll";
}
By now I shouldn't have to tell you. Using a culture that hasn't been used before in a string comparison causes an assembly load. InvariantCulture is not an exception to this.
现在我不应该告诉你。使用以前没有在字符串比较中使用过的区域性,会导致程序集加载。不变文化也不是例外。
#13
20
Here is an example of how you can create a struct that causes the error message "Attempted to read or write protected memory. This is often an indication that other memory is corrupt". The difference between success and failure is very subtle.
这里有一个示例,说明如何创建导致错误消息“试图读取或写入受保护内存”的结构。这通常表明其他记忆是腐败的。成功与失败之间的区别非常微妙。
The following unit test demonstrates the problem.
下面的单元测试演示了这个问题。
See if you can work out what went wrong.
看看你能不能算出哪里出了问题。
[Test]
public void Test()
{
var bar = new MyClass
{
Foo = 500
};
bar.Foo += 500;
Assert.That(bar.Foo.Value.Amount, Is.EqualTo(1000));
}
private class MyClass
{
public MyStruct? Foo { get; set; }
}
private struct MyStruct
{
public decimal Amount { get; private set; }
public MyStruct(decimal amount) : this()
{
Amount = amount;
}
public static MyStruct operator +(MyStruct x, MyStruct y)
{
return new MyStruct(x.Amount + y.Amount);
}
public static MyStruct operator +(MyStruct x, decimal y)
{
return new MyStruct(x.Amount + y);
}
public static implicit operator MyStruct(int value)
{
return new MyStruct(value);
}
public static implicit operator MyStruct(decimal value)
{
return new MyStruct(value);
}
}
#14
18
C# supports conversions between arrays and lists as long as the arrays are not multidimensional and there is an inheritance relation between the types and the types are reference types
c#支持数组和列表之间的转换,只要数组不是多维的,并且类型和类型之间有一个继承关系,类型是引用类型。
object[] oArray = new string[] { "one", "two", "three" };
string[] sArray = (string[])oArray;
// Also works for IList (and IEnumerable, ICollection)
IList<string> sList = (IList<string>)oArray;
IList<object> oList = new string[] { "one", "two", "three" };
Note that this does not work:
请注意,这并不起作用:
object[] oArray2 = new int[] { 1, 2, 3 }; // Error: Cannot implicitly convert type 'int[]' to 'object[]'
int[] iArray = (int[])oArray2; // Error: Cannot convert type 'object[]' to 'int[]'
#15
15
This is the strangest I've encountered by accident:
这是我偶然遇到的最奇怪的事:
public class DummyObject
{
public override string ToString()
{
return null;
}
}
Used as follows:
使用如下:
DummyObject obj = new DummyObject();
Console.WriteLine("The text: " + obj.GetType() + " is " + obj);
Will throw a NullReferenceException
. Turns out the multiple additions are compiled by the C# compiler to a call to String.Concat(object[])
. Prior to .NET 4, there is a bug in just that overload of Concat where the object is checked for null, but not the result of ToString():
会得到NullReferenceException。结果显示,由c#编译器编译的多个添加程序可以调用String.Concat(object[])。在。net 4之前,在Concat的重载中有一个bug,对象被检查为null,而不是ToString()的结果:
object obj2 = args[i];
string text = (obj2 != null) ? obj2.ToString() : string.Empty;
// if obj2 is non-null, but obj2.ToString() returns null, then text==null
int length = text.Length;
This is a bug by ECMA-334 §14.7.4:
这是ECMA-334 14.7.4的一个缺陷:
The binary + operator performs string concatenation when one or both operands are of type
string
. If an operand of string concatenation isnull
, an empty string is substituted. Otherwise, any non-string operand is converted to its string representation by invoking the virtualToString
method inherited from typeobject
. IfToString
returnsnull
, an empty string is substituted.当一个或两个操作数都是字符串时,二进制+运算符执行字符串连接。如果字符串连接的操作数为null,则替换空字符串。否则,任何非字符串操作都将通过调用从类型对象继承的虚拟ToString方法转换为其字符串表示形式。如果ToString返回null,则会替换一个空字符串。
#16
12
Interesting - when I first looked at that I assumed it was something the C# compiler was checking for, but even if you emit the IL directly to remove any chance of interference it still happens, which means it really is the newobj
op-code that's doing the checking.
有趣的是,当我第一次看到它的时候,我假设它是c#编译器正在检查的东西,但是即使你直接发射IL来消除干扰的可能性,它仍然会发生,这意味着它确实是正在进行检查的newobj op-code。
var method = new DynamicMethod("Test", null, null);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Newarr, typeof(char));
il.Emit(OpCodes.Newobj, typeof(string).GetConstructor(new[] { typeof(char[]) }));
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Newarr, typeof(char));
il.Emit(OpCodes.Newobj, typeof(string).GetConstructor(new[] { typeof(char[]) }));
il.Emit(OpCodes.Call, typeof(object).GetMethod("ReferenceEquals"));
il.Emit(OpCodes.Box, typeof(bool));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }));
il.Emit(OpCodes.Ret);
method.Invoke(null, null);
It also equates to true
if you check against string.Empty
which means this op-code must have special behaviour to intern empty strings.
如果你检查字符串,它也等于true。空的意思是,这个op-code必须有特殊的行为来处理空字符串。
#17
10
Public Class Item
Public ID As Guid
Public Text As String
Public Sub New(ByVal id As Guid, ByVal name As String)
Me.ID = id
Me.Text = name
End Sub
End Class
Public Sub Load(sender As Object, e As EventArgs) Handles Me.Load
Dim box As New ComboBox
Me.Controls.Add(box) 'Sorry I forgot this line the first time.'
Dim h As IntPtr = box.Handle 'Im not sure you need this but you might.'
Try
box.Items.Add(New Item(Guid.Empty, Nothing))
Catch ex As Exception
MsgBox(ex.ToString())
End Try
End Sub
The output is "Attempted to read protected memory. This is an indication that other memory is corrupt."
输出是“试图读取受保护的内存”。这表明其他的记忆是腐败的。
#18
10
PropertyInfo.SetValue() can assign ints to enums, ints to nullable ints, enums to nullable enums, but not ints to nullable enums.
setvalue()可以分配ints到enums, ints到nullable int, enums到nullable enums,但不是ints到nullable enums。
enumProperty.SetValue(obj, 1, null); //works
nullableIntProperty.SetValue(obj, 1, null); //works
nullableEnumProperty.SetValue(obj, MyEnum.Foo, null); //works
nullableEnumProperty.SetValue(obj, 1, null); // throws an exception !!!
Full description here
充分的描述在这里
#19
10
What if you have a generic class that has methods that could be made ambiguous depending on the type arguments? I ran into this situation recently writing a two-way dictionary. I wanted to write symmetric Get()
methods that would return the opposite of whatever argument was passed. Something like this:
如果有一个泛型类,它的方法可以根据类型参数而产生歧义,那该怎么办?我最近遇到这种情况,写了一本双向字典。我想要写对称的Get()方法,它会返回任何经过的参数的反方向。是这样的:
class TwoWayRelationship<T1, T2>
{
public T2 Get(T1 key) { /* ... */ }
public T1 Get(T2 key) { /* ... */ }
}
All is well good if you make an instance where T1
and T2
are different types:
如果你做一个T1和T2是不同类型的例子,一切都很好:
var r1 = new TwoWayRelationship<int, string>();
r1.Get(1);
r1.Get("a");
But if T1
and T2
are the same (and probably if one was a subclass of another), it's a compiler error:
但是如果T1和T2是相同的(如果一个是另一个子类的话),它就是一个编译器错误:
var r2 = new TwoWayRelationship<int, int>();
r2.Get(1); // "The call is ambiguous..."
Interestingly, all other methods in the second case are still usable; it's only calls to the now-ambiguous method that causes a compiler error. Interesting case, if a little unlikely and obscure.
有趣的是,第二种情况下的所有其他方法仍然可用;它只调用导致编译器错误的现在不明确的方法。有趣的案例,如果有点不太可能和模糊。
#20
10
C# Accessibility Puzzler
The following derived class is accessing a private field from its base class, and the compiler silently looks to the other side:
下面的派生类正在从它的基类访问一个私有字段,而编译器则静默地看着另一边:
public class Derived : Base
{
public int BrokenAccess()
{
return base.m_basePrivateField;
}
}
The field is indeed private:
这个领域确实是私人的:
private int m_basePrivateField = 0;
Care to guess how we can make such code compile?
想知道我们如何才能编译这些代码吗?
.
。
.
。
.
。
.
。
.
。
.
。
.
。
Answer
The trick is to declare Derived
as an inner class of Base
:
诀窍是声明作为一个内部类的基础:
public class Base
{
private int m_basePrivateField = 0;
public class Derived : Base
{
public int BrokenAccess()
{
return base.m_basePrivateField;
}
}
}
Inner classes are given full access to the outer class members. In this case the inner class also happens to derive from the outer class. This allows us to "break" the encapsulation of private members.
内部类可以完全访问外部类成员。在这种情况下,内部类也碰巧来自外部类。这允许我们“打破”私有成员的封装。
#21
10
Just found a nice little thing today:
今天刚刚发现了一件小事情:
public class Base
{
public virtual void Initialize(dynamic stuff) {
//...
}
}
public class Derived:Base
{
public override void Initialize(dynamic stuff) {
base.Initialize(stuff);
//...
}
}
This throws compile error.
这将编译错误。
The call to method 'Initialize' needs to be dynamically dispatched, but cannot be because it is part of a base access expression. Consider casting the dynamic arguments or eliminating the base access.
对方法“初始化”的调用需要被动态调度,但不能因为它是基本访问表达式的一部分。考虑使用动态参数或取消基本访问。
If I write base.Initialize(stuff as object); it works perfectly, however this seems to be a "magic word" here, since it does exactly the same, everything is still recieved as dynamic...
如果我写的基础。初始化(东西作为对象);它运行得很完美,不过这似乎是一个“神奇的词”,因为它完全一样,所有的东西都是动态的……
#22
8
In an API we're using, methods that return a domain object might return a special "null object". In the implementation of this, the comparison operator and the Equals()
method are overridden to return true
if it is compared with null
.
在我们使用的API中,返回域对象的方法可能返回一个特殊的“空对象”。在实现这个过程中,比较运算符和Equals()方法被重写为返回true,如果它与null比较的话。
So a user of this API might have some code like this:
这个API的用户可能有这样的代码:
return test != null ? test : GetDefault();
or perhaps a bit more verbose, like this:
或者有点啰嗦,像这样:
if (test == null)
return GetDefault();
return test;
where GetDefault()
is a method returning some default value that we want to use instead of null
. The surprise hit me when I was using ReSharper and following it's recommendation to rewrite either of this to the following:
GetDefault()是返回一些我们想要使用的默认值而不是null的方法。当我使用ReSharper的时候,我的惊讶之处在于,它的建议是将以下的内容重写为:
return test ?? GetDefault();
If the test object is a null object returned from the API instead of a proper null
, the behavior of the code has now changed, as the null coalescing operator actually checks for null
, not running operator=
or Equals()
.
如果测试对象是从API返回的空对象,而不是正确的null,那么代码的行为现在已经改变了,因为null合并操作符实际上检查的是null,而不是running operator= or Equals()。
#23
8
Consider this weird case:
考虑一下这个奇怪的情况:
public interface MyInterface {
void Method();
}
public class Base {
public void Method() { }
}
public class Derived : Base, MyInterface { }
If Base
and Derived
are declared in the same assembly, the compiler will make Base::Method
virtual and sealed (in the CIL), even though Base
doesn't implement the interface.
如果基础和派生在同一个程序集中声明,那么编译器将使Base::方法虚拟和密封(在CIL中),即使Base不实现接口。
If Base
and Derived
are in different assemblies, when compiling the Derived
assembly, the compiler won't change the other assembly, so it will introduce a member in Derived
that will be an explicit implementation for MyInterface::Method
that will just delegate the call to Base::Method
.
如果Base和派生在不同的程序集中,编译派生程序集时,编译器不会更改其他程序集,因此它将引入派生的成员,这将是MyInterface的显式实现::方法,它只会将调用委托给Base::方法。
The compiler has to do this in order to support polymorphic dispatch with regards to the interface, i.e. it has to make that method virtual.
编译器必须这样做,以支持与接口有关的多态分派,也就是说,它必须使该方法虚拟。
#24
7
The following might be general knowledge I was just simply lacking, but eh. Some time ago, we had a bug case which included virtual properties. Abstracting the context a bit, consider the following code, and apply breakpoint to specified area :
以下这些可能是我所缺乏的常识,但是。不久前,我们有一个包含虚拟属性的bug案例。将上下文抽象一下,考虑下面的代码,并将断点应用到指定的区域:
class Program
{
static void Main(string[] args)
{
Derived d = new Derived();
d.Property = "AWESOME";
}
}
class Base
{
string _baseProp;
public virtual string Property
{
get
{
return "BASE_" + _baseProp;
}
set
{
_baseProp = value;
//do work with the base property which might
//not be exposed to derived types
//here
Console.Out.WriteLine("_baseProp is BASE_" + value.ToString());
}
}
}
class Derived : Base
{
string _prop;
public override string Property
{
get { return _prop; }
set
{
_prop = value;
base.Property = value;
} //<- put a breakpoint here then mouse over BaseProperty,
// and then mouse over the base.Property call inside it.
}
public string BaseProperty { get { return base.Property; } private set { } }
}
While in the Derived
object context, you can get the same behavior when adding base.Property
as a watch, or typing base.Property
into the quickwatch.
在派生的对象上下文中,您可以在添加基础时获得相同的行为。作为手表的属性,或键入基础。quickwatch财产。
Took me some time to realize what was going on. In the end I was enlightened by the Quickwatch. When going into the Quickwatch and exploring the Derived
object d (or from the object's context, this
) and selecting the field base
, the edit field on top of the Quickwatch displays the following cast:
我花了一些时间才意识到发生了什么。最后,我受到了快表的启发。当进入Quickwatch并探索派生的对象d(或者从对象的上下文,这)并选择field base时,Quickwatch顶部的编辑字段显示以下的cast:
((TestProject1.Base)(d))
Which means that if base is replaced as such, the call would be
这意味着如果基础被替换,那么调用将会是?
public string BaseProperty { get { return ((TestProject1.Base)(d)).Property; } private set { } }
for the Watches, Quickwatch and the debugging mouse-over tooltips, and it would then make sense for it to display "AWESOME"
instead of "BASE_AWESOME"
when considering polymorphism. I'm still unsure why it would transform it into a cast, one hypothesis is that call
might not be available from those modules' context, and only callvirt
.
对于手表、Quickwatch和调试鼠标的工具提示,在考虑多态性的时候,它可以显示“AWESOME”而不是“BASE_AWESOME”。我仍然不确定为什么它会把它转换成一个cast,一个假设是调用可能不能从这些模块的context中获得,只有callvirt。
Anyhow, that obviously doesn't alter anything in terms of functionality, Derived.BaseProperty
will still really return "BASE_AWESOME"
, and thus this was not the root of our bug at work, simply a confusing component. I did however find it interesting how it could mislead developpers which would be unaware of that fact during their debug sessions, specially if Base
is not exposed in your project but rather referenced as a 3rd party DLL, resulting in Devs just saying :
无论如何,这显然不会改变任何的功能,派生的。BaseProperty仍然会真正返回“BASE_AWESOME”,因此这不是我们工作中的bug的根源,只是一个令人困惑的组件。但是,我发现它很有趣,它会误导开发人员,在调试过程中,他们不会意识到这个事实,特别是如果Base没有在项目中公开,而是被引用为第三方DLL,导致Devs只是说:
"Oi, wait..what ? omg that DLL is like, ..doing something funny"
“Oi,等. .什么?我的天啊,DLL是这样的。做一些有趣的东西”
#25
7
This one's pretty hard to top. I ran into it while I was trying to build a RealProxy implementation that truly supports Begin/EndInvoke (thanks MS for making this impossible to do without horrible hacks). This example is basically a bug in the CLR, the unmanaged code path for BeginInvoke doesn't validate that the return message from RealProxy.PrivateInvoke (and my Invoke override) is returning an instance of an IAsyncResult. Once it's returned, the CLR gets incredibly confused and loses any idea of whats going on, as demonstrated by the tests at the bottom.
这个很难顶。当我试图构建一个真正支持Begin/EndInvoke的RealProxy实现时,我遇到了这个问题(感谢MS使这个不可能做到,没有可怕的黑客)。这个示例基本上是CLR中的一个bug,而BeginInvoke的非托管代码路径并没有验证从RealProxy返回的消息。PrivateInvoke(和我的Invoke override)返回一个IAsyncResult实例。一旦它被返回,CLR就会变得难以置信的混乱,并且失去任何关于发生什么事情的想法,正如底部的测试所证明的那样。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
namespace BrokenProxy
{
class NotAnIAsyncResult
{
public string SomeProperty { get; set; }
}
class BrokenProxy : RealProxy
{
private void HackFlags()
{
var flagsField = typeof(RealProxy).GetField("_flags", BindingFlags.NonPublic | BindingFlags.Instance);
int val = (int)flagsField.GetValue(this);
val |= 1; // 1 = RemotingProxy, check out System.Runtime.Remoting.Proxies.RealProxyFlags
flagsField.SetValue(this, val);
}
public BrokenProxy(Type t)
: base(t)
{
HackFlags();
}
public override IMessage Invoke(IMessage msg)
{
var naiar = new NotAnIAsyncResult();
naiar.SomeProperty = "o noes";
return new ReturnMessage(naiar, null, 0, null, (IMethodCallMessage)msg);
}
}
interface IRandomInterface
{
int DoSomething();
}
class Program
{
static void Main(string[] args)
{
BrokenProxy bp = new BrokenProxy(typeof(IRandomInterface));
var instance = (IRandomInterface)bp.GetTransparentProxy();
Func<int> doSomethingDelegate = instance.DoSomething;
IAsyncResult notAnIAsyncResult = doSomethingDelegate.BeginInvoke(null, null);
var interfaces = notAnIAsyncResult.GetType().GetInterfaces();
Console.WriteLine(!interfaces.Any() ? "No interfaces on notAnIAsyncResult" : "Interfaces");
Console.WriteLine(notAnIAsyncResult is IAsyncResult); // Should be false, is it?!
Console.WriteLine(((NotAnIAsyncResult)notAnIAsyncResult).SomeProperty);
Console.WriteLine(((IAsyncResult)notAnIAsyncResult).IsCompleted); // No way this works.
}
}
}
Output:
输出:
No interfaces on notAnIAsyncResult
True
o noes
Unhandled Exception: System.EntryPointNotFoundException: Entry point was not found.
at System.IAsyncResult.get_IsCompleted()
at BrokenProxy.Program.Main(String[] args)
#26
6
I'm not sure if you'd say this is a Windows Vista/7 oddity or a .Net oddity but it had me scratching my head for a while.
我不知道你是否会说这是Windows Vista/7的怪异或。net的古怪,但它让我挠头了一段时间。
string filename = @"c:\program files\my folder\test.txt";
System.IO.File.WriteAllText(filename, "Hello world.");
bool exists = System.IO.File.Exists(filename); // returns true;
string text = System.IO.File.ReadAllText(filename); // Returns "Hello world."
In Windows Vista/7 the file will actually be written to C:\Users\<username>\Virtual Store\Program Files\my folder\test.txt
在Windows Vista / 7文件会被写入C:\Users\ <用户名> \虚拟商店\程序文件\ \用法我的文件夹
#27
6
Have you ever thought the C# compiler could generate invalid CIL? Run this and you'll get a TypeLoadException
:
您是否认为c#编译器可以生成无效的CIL?运行这个程序,你会得到一个类型的异常:
interface I<T> {
T M(T p);
}
abstract class A<T> : I<T> {
public abstract T M(T p);
}
abstract class B<T> : A<T>, I<int> {
public override T M(T p) { return p; }
public int M(int p) { return p * 2; }
}
class C : B<int> { }
class Program {
static void Main(string[] args) {
Console.WriteLine(new C().M(42));
}
}
I don't know how it fares in the C# 4.0 compiler though.
但我不知道它在c# 4.0编译器中是如何运行的。
EDIT: this is the output from my system:
编辑:这是我系统的输出:
C:\Temp>type Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1 {
interface I<T> {
T M(T p);
}
abstract class A<T> : I<T> {
public abstract T M(T p);
}
abstract class B<T> : A<T>, I<int> {
public override T M(T p) { return p; }
public int M(int p) { return p * 2; }
}
class C : B<int> { }
class Program {
static void Main(string[] args) {
Console.WriteLine(new C().M(11));
}
}
}
C:\Temp>csc Program.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.
C:\Temp>Program
Unhandled Exception: System.TypeLoadException: Could not load type 'ConsoleAppli
cation1.C' from assembly 'Program, Version=0.0.0.0, Culture=neutral, PublicKeyTo
ken=null'.
at ConsoleApplication1.Program.Main(String[] args)
C:\Temp>peverify Program.exe
Microsoft (R) .NET Framework PE Verifier. Version 3.5.30729.1
Copyright (c) Microsoft Corporation. All rights reserved.
[token 0x02000005] Type load failed.
[IL]: Error: [C:\Temp\Program.exe : ConsoleApplication1.Program::Main][offset 0x
00000001] Unable to resolve token.
2 Error(s) Verifying Program.exe
C:\Temp>ver
Microsoft Windows XP [Version 5.1.2600]
#28
3
There is something really exciting about C#, the way it handles closures.
c#有一些令人兴奋的地方,它处理闭包的方式。
Instead of copying the stack variable values to the closure free variable, it does that preprocessor magic wrapping all occurences of the variable into an object and thus moves it out of stack - straight to the heap! :)
它没有将堆栈变量值复制到闭包*变量,而是将变量的所有事件都封装到一个对象中,然后将其从堆栈中移出——直接移到堆中!:)
I guess, that makes C# even more functionally-complete (or lambda-complete huh)) language than ML itself (which uses stack value copying AFAIK). F# has that feature too, as C# does.
我想,这使得c#的功能比ML本身更完整(或者是lambda-complete),而不是ML本身(使用堆栈值复制AFAIK)。f#也有这个特性,就像c#一样。
That does bring much delight to me, thank you MS guys!
这让我很高兴,谢谢大家!
It's not an oddity or corner case though... but something really unexpected from a stack-based VM language :)
这不是一种奇怪的情况……但是从基于堆栈的VM语言中可以看到一些意想不到的东西:)
#29
3
From a question I asked not long ago:
不久前我问了一个问题:
Conditional operator cannot cast implicitly?
条件运算符不能隐式地转换?
Given:
考虑到:
Bool aBoolValue;
Where aBoolValue
is assigned either True or False;
其中,aBoolValue被分配为真或假;
The following will not compile:
以下将不编译:
Byte aByteValue = aBoolValue ? 1 : 0;
But this would:
但这将:
Int anIntValue = aBoolValue ? 1 : 0;
The answer provided is pretty good too.
提供的答案也相当不错。
#30
2
The scoping in c# is truly bizarre at times. Lets me give you one example:
在c#的范围内的范围是非常奇怪的。让我举一个例子:
if (true)
{
OleDbCommand command = SQLServer.CreateCommand();
}
OleDbCommand command = SQLServer.CreateCommand();
This fails to compile, because command is redeclared? There are some interested guesswork as to why it works that way in this thread on * and in my blog.
这不能编译,因为命令重新声明了?在*和我的博客中,有一些有趣的猜测是关于它为什么在这个线程中运行的。
#1
394
I think I showed you this one before, but I like the fun here - this took some debugging to track down! (the original code was obviously more complex and subtle...)
我想我之前给你们看过这个,但是我喜欢这里的乐趣-这需要一些调试来跟踪!(原来的代码显然更加复杂和微妙……)
static void Foo<T>() where T : new()
{
T t = new T();
Console.WriteLine(t.ToString()); // works fine
Console.WriteLine(t.GetHashCode()); // works fine
Console.WriteLine(t.Equals(t)); // works fine
// so it looks like an object and smells like an object...
// but this throws a NullReferenceException...
Console.WriteLine(t.GetType());
}
So what was T...
所以T是什么…
Answer: any Nullable<T>
- such as int?
. All the methods are overridden, except GetType() which can't be; so it is cast (boxed) to object (and hence to null) to call object.GetType()... which calls on null ;-p
答:任何可空的
Update: the plot thickens... Ayende Rahien threw down a similar challenge on his blog, but with a where T : class, new()
:
更新:情节变稠……Ayende Rahien在他的博客上也提出了类似的挑战,但是有一个T: class, new():
private static void Main() {
CanThisHappen<MyFunnyType>();
}
public static void CanThisHappen<T>() where T : class, new() {
var instance = new T(); // new() on a ref-type; should be non-null, then
Debug.Assert(instance != null, "How did we break the CLR?");
}
But it can be defeated! Using the same indirection used by things like remoting; warning - the following is pure evil:
但它可以被打败!使用与remoting相同的间接方法;警告-以下是纯粹的邪恶:
class MyFunnyProxyAttribute : ProxyAttribute {
public override MarshalByRefObject CreateInstance(Type serverType) {
return null;
}
}
[MyFunnyProxy]
class MyFunnyType : ContextBoundObject { }
With this in place, the new()
call is redirected to the proxy (MyFunnyProxyAttribute
), which returns null
. Now go and wash your eyes!
这样,新的()调用被重定向到代理(MyFunnyProxyAttribute),该代理返回null。现在去洗你的眼睛吧!
#2
216
Bankers' Rounding.
银行家的舍入。
This one is not so much a compiler bug or malfunction, but certainly a strange corner case...
这不是一个编译器错误或故障,但肯定是一个奇怪的角落。
The .Net Framework employs a scheme or rounding known as Banker's Rounding.
. net框架采用了一种名为“银行家四舍五入”的方案。
In Bankers' Rounding the 0.5 numbers are rounded to the nearest even number, so
在银行家的四舍五入中,0.5个数字被四舍五入到最近的偶数,所以。
Math.Round(-0.5) == 0
Math.Round(0.5) == 0
Math.Round(1.5) == 2
Math.Round(2.5) == 2
etc...
This can lead to some unexpected bugs in financial calculations based on the more well known Round-Half-Up rounding.
这可能会导致一些意料之外的财务计算错误,这些错误是基于更广为人知的圆半整数的四舍五入。
This is also true of Visual Basic.
这在视觉基础上也是正确的。
#3
176
What will this function do if called as Rec(0)
(not under the debugger)?
如果调用Rec(0)(不在调试器下),该函数会做什么?
static void Rec(int i)
{
Console.WriteLine(i);
if (i < int.MaxValue)
{
Rec(i + 1);
}
}
Answer:
答:
- On 32-bit JIT it should result in a *Exception
- 在32位JIT上,它应该导致*Exception。
- On 64-bit JIT it should print all the numbers to int.MaxValue
- 在64位JIT上,它应该将所有数字打印到int.MaxValue。
This is because the 64-bit JIT compiler applies tail call optimisation, whereas the 32-bit JIT does not.
这是因为64位JIT编译器应用尾部调用优化,而32位JIT编译器则没有。
Unfortunately I haven't got a 64-bit machine to hand to verify this, but the method does meet all the conditions for tail-call optimisation. If anybody does have one I'd be interested to see if it's true.
不幸的是,我还没有一个64位的机器来验证这一点,但是这个方法确实满足了对尾调用优化的所有条件。如果有人有的话,我很想看看是不是真的。
#4
112
Assign This!
This is one that I like to ask at parties (which is probably why I don't get invited anymore):
这是我在派对上喜欢问的问题(这可能是我不再被邀请的原因):
Can you make the following piece of code compile?
你能做下面的代码编译吗?
public void Foo()
{
this = new Teaser();
}
An easy cheat could be:
一个简单的欺骗可以是:
string cheat = @"
public void Foo()
{
this = new Teaser();
}
";
But the real solution is this:
但真正的解决方法是:
public struct Teaser
{
public void Foo()
{
this = new Teaser();
}
}
So it's a little know fact that value types (structs) can reassign their this
variable.
因此,有一点知道,值类型(struct)可以重新分配这个变量。
#5
100
Few years ago, when working on loyality program, we had an issue with the amount of points given to customers. The issue was related to casting/converting double to int.
几年前,在“忠诚计划”(loyality program)工作的时候,我们遇到了一个问题,那就是给客户的分数是多少。这个问题与铸造/转换成整数有关。
In code below:
下面的代码:
double d = 13.6;
int i1 = Convert.ToInt32(d);
int i2 = (int)d;
does i1 == i2 ?
i1 == i2 ?
It turns out that i1 != i2. Because of different rounding policies in Convert and cast operator the actual values are:
结果是i1 != i2。由于转换和转换算子的舍入策略不同,实际值为:
i1 == 14
i2 == 13
It's always better to call Math.Ceiling() or Math.Floor() (or Math.Round with MidpointRounding that meets our requirements)
最好调用Math. ceiling()或Math. floor()(或Math)。以符合我们要求的中点为圆心
int i1 = Convert.ToInt32( Math.Ceiling(d) );
int i2 = (int) Math.Ceiling(d);
#6
74
They should have made 0 an integer even when there's an enum function overload.
即使有枚举函数重载,它们也应该使0为整数。
I knew C# core team rationale for mapping 0 to enum, but still, it is not as orthogonal as it should be. Example from Npgsql.
我知道将0映射到enum的c#核心团队的基本原理,但它仍然不像它应该的那样正交。来自Npgsql示例。
Test example:
测试的例子:
namespace Craft
{
enum Symbol { Alpha = 1, Beta = 2, Gamma = 3, Delta = 4 };
class Mate
{
static void Main(string[] args)
{
JustTest(Symbol.Alpha); // enum
JustTest(0); // why enum
JustTest((int)0); // why still enum
int i = 0;
JustTest(Convert.ToInt32(0)); // have to use Convert.ToInt32 to convince the compiler to make the call site use the object version
JustTest(i); // it's ok from down here and below
JustTest(1);
JustTest("string");
JustTest(Guid.NewGuid());
JustTest(new DataTable());
Console.ReadLine();
}
static void JustTest(Symbol a)
{
Console.WriteLine("Enum");
}
static void JustTest(object o)
{
Console.WriteLine("Object");
}
}
}
#7
67
This is one of the most unusual i've seen so far (aside from the ones here of course!):
这是迄今为止我见过的最不寻常的事情之一(当然,除了这里的那些!)
public class Turtle<T> where T : Turtle<T>
{
}
It lets you declare it but has no real use, since it will always ask you to wrap whatever class you stuff in the center with another Turtle.
它可以让你声明它,但没有实际用途,因为它总是要求你把你在中心的东西和另一只乌龟包在一起。
[joke] I guess it's turtles all the way down... [/joke]
[笑话]我猜是海龟一路下来……[/笑话]
#8
65
Here's one I only found out about recently...
这是我最近才发现的一个…
interface IFoo
{
string Message {get;}
}
...
IFoo obj = new IFoo("abc");
Console.WriteLine(obj.Message);
The above looks crazy at first glance, but is actually legal.No, really (although I've missed out a key part, but it isn't anything hacky like "add a class called IFoo
" or "add a using
alias to point IFoo
at a class").
乍一看,上面看起来很疯狂,但实际上是合法的。不,真的(虽然我遗漏了一个关键部分,但它并不是像“添加一个名为IFoo的类”或“在类中添加一个使用别名来指向IFoo”)。
See if you can figure out why, then: Who says you can’t instantiate an interface?
看看你能否找出原因:谁说你不能实例化一个接口?
#9
56
When is a Boolean neither True nor False?
什么时候一个布尔值既不正确也不假?
Bill discovered that you can hack a boolean so that if A is True and B is True, (A and B) is False.
比尔发现你可以破解一个布尔值,这样如果a为真,B为真,(a和B)为假。
砍布尔值
#10
47
I'm arriving a bit late to the party, but I've got
three
four
five:
我来参加派对有点晚了,但我有3个4个5个:
-
If you poll InvokeRequired on a control that hasn't been loaded/shown, it will say false - and blow up in your face if you try to change it from another thread (the solution is to reference this.Handle in the creator of the control).
如果你在一个没有加载/显示的控件上进行了调用,它会说错误,如果你试图从另一个线程中改变它,那么你的脸就会被炸飞(解决方案是引用这个)。在控制的创建者的句柄。
-
Another one which tripped me up is that given an assembly with:
另一个让我绊倒的是,给了一个装配:
enum MyEnum { Red, Blue, }
if you calculate MyEnum.Red.ToString() in another assembly, and in between times someone has recompiled your enum to:
如果您在另一个程序集中计算MyEnum.Red.ToString(),并且在此期间,有人将您的enum重新编译为:
enum MyEnum { Black, Red, Blue, }
at runtime, you will get "Black".
在运行时,您将得到“黑色”。
-
I had a shared assembly with some handy constants in. My predecessor had left a load of ugly-looking get-only properties, I thought I'd get rid of the clutter and just use public const. I was more than a little surprised when VS compiled them to their values, and not references.
我有一个共享程序集,里面有一些方便的常数。我的前任留下了一大堆丑陋的“get-only”属性,我想我应该摆脱混乱,只使用公共的const。当VS将它们编译成它们的值,而不是引用时,我感到有点惊讶。
-
If you implement a new method of an interface from another assembly, but you rebuild referencing the old version of that assembly, you get a TypeLoadException (no implementation of 'NewMethod'), even though you have implemented it (see here).
如果您从另一个程序集实现一个接口的新方法,但是您重新引用了该程序集的旧版本,那么您就得到了一个TypeLoadException(没有“NewMethod”的实现),即使您已经实现了它(参见这里)。
-
Dictionary<,>: "The order in which the items are returned is undefined". This is horrible, because it can bite you sometimes, but work others, and if you've just blindly assumed that Dictionary is going to play nice ("why shouldn't it? I thought, List does"), you really have to have your nose in it before you finally start to question your assumption.
字典<,>:“条目返回的顺序是未定义的”。这很可怕,因为它有时会咬你,但也会影响别人,如果你只是盲目地认为字典会很好(“为什么?”在你开始质疑你的假设之前,你真的必须要有自己的鼻子。
#11
33
VB.NET, nullables and the ternary operator:
VB。NET、nullables和ternary操作符:
Dim i As Integer? = If(True, Nothing, 5)
This took me some time to debug, since I expected i
to contain Nothing
.
这花了我一些时间调试,因为我希望我什么都不包含。
What does i really contain? 0
.
我到底包含了什么?0。
This is surprising but actually "correct" behavior: Nothing
in VB.NET is not exactly the same as null
in CLR: Nothing
can either mean null
or default(T)
for a value type T
, depending on the context. In the above case, If
infers Integer
as the common type of Nothing
and 5
, so, in this case, Nothing
means 0
.
这很奇怪,但实际上是“正确”的行为:VB中没有。在CLR中,NET并不完全等同于null:对于一个值类型T,任何东西都不能表示null或默认值(T),这取决于上下文。在上面的例子中,如果infers Integer是普通类型的Nothing和5,那么,在这种情况下,什么都不是0。
#12
28
I found a second really strange corner case that beats my first one by a long shot.
我发现了另一个非常奇怪的角落,它比我的第一个要远得多。
String.Equals Method (String, String, StringComparison) is not actually side effect free.
字符串。Equals方法(String, String, StringComparison)实际上并不是免费的。
I was working on a block of code that had this on a line by itself at the top of some function:
我在做一个代码块,它在某个函数的顶部有一个这样的代码:
stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase);
Removing that line lead to a stack overflow somewhere else in the program.
删除这一行会导致程序中其他地方的堆栈溢出。
The code turned out to be installing a handler for what was in essence a BeforeAssemblyLoad event and trying to do
代码原来是安装一个处理程序,其本质上是一个beforeassembly加载事件和尝试做的事情。
if (assemblyfilename.EndsWith("someparticular.dll", StringComparison.InvariantCultureIgnoreCase))
{
assemblyfilename = "someparticular_modified.dll";
}
By now I shouldn't have to tell you. Using a culture that hasn't been used before in a string comparison causes an assembly load. InvariantCulture is not an exception to this.
现在我不应该告诉你。使用以前没有在字符串比较中使用过的区域性,会导致程序集加载。不变文化也不是例外。
#13
20
Here is an example of how you can create a struct that causes the error message "Attempted to read or write protected memory. This is often an indication that other memory is corrupt". The difference between success and failure is very subtle.
这里有一个示例,说明如何创建导致错误消息“试图读取或写入受保护内存”的结构。这通常表明其他记忆是腐败的。成功与失败之间的区别非常微妙。
The following unit test demonstrates the problem.
下面的单元测试演示了这个问题。
See if you can work out what went wrong.
看看你能不能算出哪里出了问题。
[Test]
public void Test()
{
var bar = new MyClass
{
Foo = 500
};
bar.Foo += 500;
Assert.That(bar.Foo.Value.Amount, Is.EqualTo(1000));
}
private class MyClass
{
public MyStruct? Foo { get; set; }
}
private struct MyStruct
{
public decimal Amount { get; private set; }
public MyStruct(decimal amount) : this()
{
Amount = amount;
}
public static MyStruct operator +(MyStruct x, MyStruct y)
{
return new MyStruct(x.Amount + y.Amount);
}
public static MyStruct operator +(MyStruct x, decimal y)
{
return new MyStruct(x.Amount + y);
}
public static implicit operator MyStruct(int value)
{
return new MyStruct(value);
}
public static implicit operator MyStruct(decimal value)
{
return new MyStruct(value);
}
}
#14
18
C# supports conversions between arrays and lists as long as the arrays are not multidimensional and there is an inheritance relation between the types and the types are reference types
c#支持数组和列表之间的转换,只要数组不是多维的,并且类型和类型之间有一个继承关系,类型是引用类型。
object[] oArray = new string[] { "one", "two", "three" };
string[] sArray = (string[])oArray;
// Also works for IList (and IEnumerable, ICollection)
IList<string> sList = (IList<string>)oArray;
IList<object> oList = new string[] { "one", "two", "three" };
Note that this does not work:
请注意,这并不起作用:
object[] oArray2 = new int[] { 1, 2, 3 }; // Error: Cannot implicitly convert type 'int[]' to 'object[]'
int[] iArray = (int[])oArray2; // Error: Cannot convert type 'object[]' to 'int[]'
#15
15
This is the strangest I've encountered by accident:
这是我偶然遇到的最奇怪的事:
public class DummyObject
{
public override string ToString()
{
return null;
}
}
Used as follows:
使用如下:
DummyObject obj = new DummyObject();
Console.WriteLine("The text: " + obj.GetType() + " is " + obj);
Will throw a NullReferenceException
. Turns out the multiple additions are compiled by the C# compiler to a call to String.Concat(object[])
. Prior to .NET 4, there is a bug in just that overload of Concat where the object is checked for null, but not the result of ToString():
会得到NullReferenceException。结果显示,由c#编译器编译的多个添加程序可以调用String.Concat(object[])。在。net 4之前,在Concat的重载中有一个bug,对象被检查为null,而不是ToString()的结果:
object obj2 = args[i];
string text = (obj2 != null) ? obj2.ToString() : string.Empty;
// if obj2 is non-null, but obj2.ToString() returns null, then text==null
int length = text.Length;
This is a bug by ECMA-334 §14.7.4:
这是ECMA-334 14.7.4的一个缺陷:
The binary + operator performs string concatenation when one or both operands are of type
string
. If an operand of string concatenation isnull
, an empty string is substituted. Otherwise, any non-string operand is converted to its string representation by invoking the virtualToString
method inherited from typeobject
. IfToString
returnsnull
, an empty string is substituted.当一个或两个操作数都是字符串时,二进制+运算符执行字符串连接。如果字符串连接的操作数为null,则替换空字符串。否则,任何非字符串操作都将通过调用从类型对象继承的虚拟ToString方法转换为其字符串表示形式。如果ToString返回null,则会替换一个空字符串。
#16
12
Interesting - when I first looked at that I assumed it was something the C# compiler was checking for, but even if you emit the IL directly to remove any chance of interference it still happens, which means it really is the newobj
op-code that's doing the checking.
有趣的是,当我第一次看到它的时候,我假设它是c#编译器正在检查的东西,但是即使你直接发射IL来消除干扰的可能性,它仍然会发生,这意味着它确实是正在进行检查的newobj op-code。
var method = new DynamicMethod("Test", null, null);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Newarr, typeof(char));
il.Emit(OpCodes.Newobj, typeof(string).GetConstructor(new[] { typeof(char[]) }));
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Newarr, typeof(char));
il.Emit(OpCodes.Newobj, typeof(string).GetConstructor(new[] { typeof(char[]) }));
il.Emit(OpCodes.Call, typeof(object).GetMethod("ReferenceEquals"));
il.Emit(OpCodes.Box, typeof(bool));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }));
il.Emit(OpCodes.Ret);
method.Invoke(null, null);
It also equates to true
if you check against string.Empty
which means this op-code must have special behaviour to intern empty strings.
如果你检查字符串,它也等于true。空的意思是,这个op-code必须有特殊的行为来处理空字符串。
#17
10
Public Class Item
Public ID As Guid
Public Text As String
Public Sub New(ByVal id As Guid, ByVal name As String)
Me.ID = id
Me.Text = name
End Sub
End Class
Public Sub Load(sender As Object, e As EventArgs) Handles Me.Load
Dim box As New ComboBox
Me.Controls.Add(box) 'Sorry I forgot this line the first time.'
Dim h As IntPtr = box.Handle 'Im not sure you need this but you might.'
Try
box.Items.Add(New Item(Guid.Empty, Nothing))
Catch ex As Exception
MsgBox(ex.ToString())
End Try
End Sub
The output is "Attempted to read protected memory. This is an indication that other memory is corrupt."
输出是“试图读取受保护的内存”。这表明其他的记忆是腐败的。
#18
10
PropertyInfo.SetValue() can assign ints to enums, ints to nullable ints, enums to nullable enums, but not ints to nullable enums.
setvalue()可以分配ints到enums, ints到nullable int, enums到nullable enums,但不是ints到nullable enums。
enumProperty.SetValue(obj, 1, null); //works
nullableIntProperty.SetValue(obj, 1, null); //works
nullableEnumProperty.SetValue(obj, MyEnum.Foo, null); //works
nullableEnumProperty.SetValue(obj, 1, null); // throws an exception !!!
Full description here
充分的描述在这里
#19
10
What if you have a generic class that has methods that could be made ambiguous depending on the type arguments? I ran into this situation recently writing a two-way dictionary. I wanted to write symmetric Get()
methods that would return the opposite of whatever argument was passed. Something like this:
如果有一个泛型类,它的方法可以根据类型参数而产生歧义,那该怎么办?我最近遇到这种情况,写了一本双向字典。我想要写对称的Get()方法,它会返回任何经过的参数的反方向。是这样的:
class TwoWayRelationship<T1, T2>
{
public T2 Get(T1 key) { /* ... */ }
public T1 Get(T2 key) { /* ... */ }
}
All is well good if you make an instance where T1
and T2
are different types:
如果你做一个T1和T2是不同类型的例子,一切都很好:
var r1 = new TwoWayRelationship<int, string>();
r1.Get(1);
r1.Get("a");
But if T1
and T2
are the same (and probably if one was a subclass of another), it's a compiler error:
但是如果T1和T2是相同的(如果一个是另一个子类的话),它就是一个编译器错误:
var r2 = new TwoWayRelationship<int, int>();
r2.Get(1); // "The call is ambiguous..."
Interestingly, all other methods in the second case are still usable; it's only calls to the now-ambiguous method that causes a compiler error. Interesting case, if a little unlikely and obscure.
有趣的是,第二种情况下的所有其他方法仍然可用;它只调用导致编译器错误的现在不明确的方法。有趣的案例,如果有点不太可能和模糊。
#20
10
C# Accessibility Puzzler
The following derived class is accessing a private field from its base class, and the compiler silently looks to the other side:
下面的派生类正在从它的基类访问一个私有字段,而编译器则静默地看着另一边:
public class Derived : Base
{
public int BrokenAccess()
{
return base.m_basePrivateField;
}
}
The field is indeed private:
这个领域确实是私人的:
private int m_basePrivateField = 0;
Care to guess how we can make such code compile?
想知道我们如何才能编译这些代码吗?
.
。
.
。
.
。
.
。
.
。
.
。
.
。
Answer
The trick is to declare Derived
as an inner class of Base
:
诀窍是声明作为一个内部类的基础:
public class Base
{
private int m_basePrivateField = 0;
public class Derived : Base
{
public int BrokenAccess()
{
return base.m_basePrivateField;
}
}
}
Inner classes are given full access to the outer class members. In this case the inner class also happens to derive from the outer class. This allows us to "break" the encapsulation of private members.
内部类可以完全访问外部类成员。在这种情况下,内部类也碰巧来自外部类。这允许我们“打破”私有成员的封装。
#21
10
Just found a nice little thing today:
今天刚刚发现了一件小事情:
public class Base
{
public virtual void Initialize(dynamic stuff) {
//...
}
}
public class Derived:Base
{
public override void Initialize(dynamic stuff) {
base.Initialize(stuff);
//...
}
}
This throws compile error.
这将编译错误。
The call to method 'Initialize' needs to be dynamically dispatched, but cannot be because it is part of a base access expression. Consider casting the dynamic arguments or eliminating the base access.
对方法“初始化”的调用需要被动态调度,但不能因为它是基本访问表达式的一部分。考虑使用动态参数或取消基本访问。
If I write base.Initialize(stuff as object); it works perfectly, however this seems to be a "magic word" here, since it does exactly the same, everything is still recieved as dynamic...
如果我写的基础。初始化(东西作为对象);它运行得很完美,不过这似乎是一个“神奇的词”,因为它完全一样,所有的东西都是动态的……
#22
8
In an API we're using, methods that return a domain object might return a special "null object". In the implementation of this, the comparison operator and the Equals()
method are overridden to return true
if it is compared with null
.
在我们使用的API中,返回域对象的方法可能返回一个特殊的“空对象”。在实现这个过程中,比较运算符和Equals()方法被重写为返回true,如果它与null比较的话。
So a user of this API might have some code like this:
这个API的用户可能有这样的代码:
return test != null ? test : GetDefault();
or perhaps a bit more verbose, like this:
或者有点啰嗦,像这样:
if (test == null)
return GetDefault();
return test;
where GetDefault()
is a method returning some default value that we want to use instead of null
. The surprise hit me when I was using ReSharper and following it's recommendation to rewrite either of this to the following:
GetDefault()是返回一些我们想要使用的默认值而不是null的方法。当我使用ReSharper的时候,我的惊讶之处在于,它的建议是将以下的内容重写为:
return test ?? GetDefault();
If the test object is a null object returned from the API instead of a proper null
, the behavior of the code has now changed, as the null coalescing operator actually checks for null
, not running operator=
or Equals()
.
如果测试对象是从API返回的空对象,而不是正确的null,那么代码的行为现在已经改变了,因为null合并操作符实际上检查的是null,而不是running operator= or Equals()。
#23
8
Consider this weird case:
考虑一下这个奇怪的情况:
public interface MyInterface {
void Method();
}
public class Base {
public void Method() { }
}
public class Derived : Base, MyInterface { }
If Base
and Derived
are declared in the same assembly, the compiler will make Base::Method
virtual and sealed (in the CIL), even though Base
doesn't implement the interface.
如果基础和派生在同一个程序集中声明,那么编译器将使Base::方法虚拟和密封(在CIL中),即使Base不实现接口。
If Base
and Derived
are in different assemblies, when compiling the Derived
assembly, the compiler won't change the other assembly, so it will introduce a member in Derived
that will be an explicit implementation for MyInterface::Method
that will just delegate the call to Base::Method
.
如果Base和派生在不同的程序集中,编译派生程序集时,编译器不会更改其他程序集,因此它将引入派生的成员,这将是MyInterface的显式实现::方法,它只会将调用委托给Base::方法。
The compiler has to do this in order to support polymorphic dispatch with regards to the interface, i.e. it has to make that method virtual.
编译器必须这样做,以支持与接口有关的多态分派,也就是说,它必须使该方法虚拟。
#24
7
The following might be general knowledge I was just simply lacking, but eh. Some time ago, we had a bug case which included virtual properties. Abstracting the context a bit, consider the following code, and apply breakpoint to specified area :
以下这些可能是我所缺乏的常识,但是。不久前,我们有一个包含虚拟属性的bug案例。将上下文抽象一下,考虑下面的代码,并将断点应用到指定的区域:
class Program
{
static void Main(string[] args)
{
Derived d = new Derived();
d.Property = "AWESOME";
}
}
class Base
{
string _baseProp;
public virtual string Property
{
get
{
return "BASE_" + _baseProp;
}
set
{
_baseProp = value;
//do work with the base property which might
//not be exposed to derived types
//here
Console.Out.WriteLine("_baseProp is BASE_" + value.ToString());
}
}
}
class Derived : Base
{
string _prop;
public override string Property
{
get { return _prop; }
set
{
_prop = value;
base.Property = value;
} //<- put a breakpoint here then mouse over BaseProperty,
// and then mouse over the base.Property call inside it.
}
public string BaseProperty { get { return base.Property; } private set { } }
}
While in the Derived
object context, you can get the same behavior when adding base.Property
as a watch, or typing base.Property
into the quickwatch.
在派生的对象上下文中,您可以在添加基础时获得相同的行为。作为手表的属性,或键入基础。quickwatch财产。
Took me some time to realize what was going on. In the end I was enlightened by the Quickwatch. When going into the Quickwatch and exploring the Derived
object d (or from the object's context, this
) and selecting the field base
, the edit field on top of the Quickwatch displays the following cast:
我花了一些时间才意识到发生了什么。最后,我受到了快表的启发。当进入Quickwatch并探索派生的对象d(或者从对象的上下文,这)并选择field base时,Quickwatch顶部的编辑字段显示以下的cast:
((TestProject1.Base)(d))
Which means that if base is replaced as such, the call would be
这意味着如果基础被替换,那么调用将会是?
public string BaseProperty { get { return ((TestProject1.Base)(d)).Property; } private set { } }
for the Watches, Quickwatch and the debugging mouse-over tooltips, and it would then make sense for it to display "AWESOME"
instead of "BASE_AWESOME"
when considering polymorphism. I'm still unsure why it would transform it into a cast, one hypothesis is that call
might not be available from those modules' context, and only callvirt
.
对于手表、Quickwatch和调试鼠标的工具提示,在考虑多态性的时候,它可以显示“AWESOME”而不是“BASE_AWESOME”。我仍然不确定为什么它会把它转换成一个cast,一个假设是调用可能不能从这些模块的context中获得,只有callvirt。
Anyhow, that obviously doesn't alter anything in terms of functionality, Derived.BaseProperty
will still really return "BASE_AWESOME"
, and thus this was not the root of our bug at work, simply a confusing component. I did however find it interesting how it could mislead developpers which would be unaware of that fact during their debug sessions, specially if Base
is not exposed in your project but rather referenced as a 3rd party DLL, resulting in Devs just saying :
无论如何,这显然不会改变任何的功能,派生的。BaseProperty仍然会真正返回“BASE_AWESOME”,因此这不是我们工作中的bug的根源,只是一个令人困惑的组件。但是,我发现它很有趣,它会误导开发人员,在调试过程中,他们不会意识到这个事实,特别是如果Base没有在项目中公开,而是被引用为第三方DLL,导致Devs只是说:
"Oi, wait..what ? omg that DLL is like, ..doing something funny"
“Oi,等. .什么?我的天啊,DLL是这样的。做一些有趣的东西”
#25
7
This one's pretty hard to top. I ran into it while I was trying to build a RealProxy implementation that truly supports Begin/EndInvoke (thanks MS for making this impossible to do without horrible hacks). This example is basically a bug in the CLR, the unmanaged code path for BeginInvoke doesn't validate that the return message from RealProxy.PrivateInvoke (and my Invoke override) is returning an instance of an IAsyncResult. Once it's returned, the CLR gets incredibly confused and loses any idea of whats going on, as demonstrated by the tests at the bottom.
这个很难顶。当我试图构建一个真正支持Begin/EndInvoke的RealProxy实现时,我遇到了这个问题(感谢MS使这个不可能做到,没有可怕的黑客)。这个示例基本上是CLR中的一个bug,而BeginInvoke的非托管代码路径并没有验证从RealProxy返回的消息。PrivateInvoke(和我的Invoke override)返回一个IAsyncResult实例。一旦它被返回,CLR就会变得难以置信的混乱,并且失去任何关于发生什么事情的想法,正如底部的测试所证明的那样。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
namespace BrokenProxy
{
class NotAnIAsyncResult
{
public string SomeProperty { get; set; }
}
class BrokenProxy : RealProxy
{
private void HackFlags()
{
var flagsField = typeof(RealProxy).GetField("_flags", BindingFlags.NonPublic | BindingFlags.Instance);
int val = (int)flagsField.GetValue(this);
val |= 1; // 1 = RemotingProxy, check out System.Runtime.Remoting.Proxies.RealProxyFlags
flagsField.SetValue(this, val);
}
public BrokenProxy(Type t)
: base(t)
{
HackFlags();
}
public override IMessage Invoke(IMessage msg)
{
var naiar = new NotAnIAsyncResult();
naiar.SomeProperty = "o noes";
return new ReturnMessage(naiar, null, 0, null, (IMethodCallMessage)msg);
}
}
interface IRandomInterface
{
int DoSomething();
}
class Program
{
static void Main(string[] args)
{
BrokenProxy bp = new BrokenProxy(typeof(IRandomInterface));
var instance = (IRandomInterface)bp.GetTransparentProxy();
Func<int> doSomethingDelegate = instance.DoSomething;
IAsyncResult notAnIAsyncResult = doSomethingDelegate.BeginInvoke(null, null);
var interfaces = notAnIAsyncResult.GetType().GetInterfaces();
Console.WriteLine(!interfaces.Any() ? "No interfaces on notAnIAsyncResult" : "Interfaces");
Console.WriteLine(notAnIAsyncResult is IAsyncResult); // Should be false, is it?!
Console.WriteLine(((NotAnIAsyncResult)notAnIAsyncResult).SomeProperty);
Console.WriteLine(((IAsyncResult)notAnIAsyncResult).IsCompleted); // No way this works.
}
}
}
Output:
输出:
No interfaces on notAnIAsyncResult
True
o noes
Unhandled Exception: System.EntryPointNotFoundException: Entry point was not found.
at System.IAsyncResult.get_IsCompleted()
at BrokenProxy.Program.Main(String[] args)
#26
6
I'm not sure if you'd say this is a Windows Vista/7 oddity or a .Net oddity but it had me scratching my head for a while.
我不知道你是否会说这是Windows Vista/7的怪异或。net的古怪,但它让我挠头了一段时间。
string filename = @"c:\program files\my folder\test.txt";
System.IO.File.WriteAllText(filename, "Hello world.");
bool exists = System.IO.File.Exists(filename); // returns true;
string text = System.IO.File.ReadAllText(filename); // Returns "Hello world."
In Windows Vista/7 the file will actually be written to C:\Users\<username>\Virtual Store\Program Files\my folder\test.txt
在Windows Vista / 7文件会被写入C:\Users\ <用户名> \虚拟商店\程序文件\ \用法我的文件夹
#27
6
Have you ever thought the C# compiler could generate invalid CIL? Run this and you'll get a TypeLoadException
:
您是否认为c#编译器可以生成无效的CIL?运行这个程序,你会得到一个类型的异常:
interface I<T> {
T M(T p);
}
abstract class A<T> : I<T> {
public abstract T M(T p);
}
abstract class B<T> : A<T>, I<int> {
public override T M(T p) { return p; }
public int M(int p) { return p * 2; }
}
class C : B<int> { }
class Program {
static void Main(string[] args) {
Console.WriteLine(new C().M(42));
}
}
I don't know how it fares in the C# 4.0 compiler though.
但我不知道它在c# 4.0编译器中是如何运行的。
EDIT: this is the output from my system:
编辑:这是我系统的输出:
C:\Temp>type Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1 {
interface I<T> {
T M(T p);
}
abstract class A<T> : I<T> {
public abstract T M(T p);
}
abstract class B<T> : A<T>, I<int> {
public override T M(T p) { return p; }
public int M(int p) { return p * 2; }
}
class C : B<int> { }
class Program {
static void Main(string[] args) {
Console.WriteLine(new C().M(11));
}
}
}
C:\Temp>csc Program.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.
C:\Temp>Program
Unhandled Exception: System.TypeLoadException: Could not load type 'ConsoleAppli
cation1.C' from assembly 'Program, Version=0.0.0.0, Culture=neutral, PublicKeyTo
ken=null'.
at ConsoleApplication1.Program.Main(String[] args)
C:\Temp>peverify Program.exe
Microsoft (R) .NET Framework PE Verifier. Version 3.5.30729.1
Copyright (c) Microsoft Corporation. All rights reserved.
[token 0x02000005] Type load failed.
[IL]: Error: [C:\Temp\Program.exe : ConsoleApplication1.Program::Main][offset 0x
00000001] Unable to resolve token.
2 Error(s) Verifying Program.exe
C:\Temp>ver
Microsoft Windows XP [Version 5.1.2600]
#28
3
There is something really exciting about C#, the way it handles closures.
c#有一些令人兴奋的地方,它处理闭包的方式。
Instead of copying the stack variable values to the closure free variable, it does that preprocessor magic wrapping all occurences of the variable into an object and thus moves it out of stack - straight to the heap! :)
它没有将堆栈变量值复制到闭包*变量,而是将变量的所有事件都封装到一个对象中,然后将其从堆栈中移出——直接移到堆中!:)
I guess, that makes C# even more functionally-complete (or lambda-complete huh)) language than ML itself (which uses stack value copying AFAIK). F# has that feature too, as C# does.
我想,这使得c#的功能比ML本身更完整(或者是lambda-complete),而不是ML本身(使用堆栈值复制AFAIK)。f#也有这个特性,就像c#一样。
That does bring much delight to me, thank you MS guys!
这让我很高兴,谢谢大家!
It's not an oddity or corner case though... but something really unexpected from a stack-based VM language :)
这不是一种奇怪的情况……但是从基于堆栈的VM语言中可以看到一些意想不到的东西:)
#29
3
From a question I asked not long ago:
不久前我问了一个问题:
Conditional operator cannot cast implicitly?
条件运算符不能隐式地转换?
Given:
考虑到:
Bool aBoolValue;
Where aBoolValue
is assigned either True or False;
其中,aBoolValue被分配为真或假;
The following will not compile:
以下将不编译:
Byte aByteValue = aBoolValue ? 1 : 0;
But this would:
但这将:
Int anIntValue = aBoolValue ? 1 : 0;
The answer provided is pretty good too.
提供的答案也相当不错。
#30
2
The scoping in c# is truly bizarre at times. Lets me give you one example:
在c#的范围内的范围是非常奇怪的。让我举一个例子:
if (true)
{
OleDbCommand command = SQLServer.CreateCommand();
}
OleDbCommand command = SQLServer.CreateCommand();
This fails to compile, because command is redeclared? There are some interested guesswork as to why it works that way in this thread on * and in my blog.
这不能编译,因为命令重新声明了?在*和我的博客中,有一些有趣的猜测是关于它为什么在这个线程中运行的。