我收集了一些极端案例和脑筋急转弯,总是想听到更多。这个页面只涵盖了c#语言的一些细节,但我也发现了。net核心的东西也很有趣。例如,这里有一个没有在页面上,但我觉得不可思议:
string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x, y));
我希望输出False -毕竟,“new”(具有引用类型)总是创建一个新对象,不是吗?c#和CLI的规范都表明应该这样做。嗯,在这个特殊情况下不是这样。它输出True,并且在我测试过的框架的每个版本上都是这样。(不可否认,我还没有在Mono上尝试过……)
只是为了澄清,这只是我正在寻找的事情的一个例子-我并不是特别寻找对这个奇怪现象的讨论/解释。(这和普通的弦乐实习不一样;特别地,当调用构造函数时,字符串实习通常不会发生。)我真的是在要求类似的奇怪行为。
还有其他的宝藏吗?
想想这个奇怪的例子:
public interface MyInterface {
void Method();
}
public class Base {
public void Method() { }
}
public class Derived : Base, MyInterface { }
如果在同一个程序集中声明Base和Derived,编译器将使Base::方法为虚拟且密封(在CIL中),即使Base没有实现接口。
如果Base和Derived在不同的程序集中,编译Derived程序集时,编译器不会更改其他程序集,因此它将在Derived中引入一个成员,该成员将是MyInterface::Method的显式实现,它只会将调用委托给Base::Method。
编译器必须这样做,以支持与接口有关的多态分派,即它必须使该方法为虚拟。
这是我无意中遇到的最奇怪的事情:
public class DummyObject
{
public override string ToString()
{
return null;
}
}
用途如下:
DummyObject obj = new DummyObject();
Console.WriteLine("The text: " + obj.GetType() + " is " + obj);
将抛出NullReferenceException。事实证明,c#编译器将多个添加编译为对String.Concat(object[])的调用。在。net 4之前,在Concat重载中有一个错误,其中对象被检查为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;
这是ECMA-334§14.7.4的错误:
当一个或两个操作数都是字符串类型时,binary +操作符执行字符串连接。如果字符串连接的操作数为空,则替换为空字符串。否则,任何非字符串操作数都将通过调用继承自type object的虚拟ToString方法转换为其字符串表示形式。如果ToString返回null,则替换为空字符串。
我发现了第二个非常奇怪的极端情况,远远超过了我的第一个。
字符串。Equals Method (String, String, StringComparison)实际上并不是没有副作用的。
我当时在写一段代码,在某个函数的顶部有一行这样的代码:
stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase);
删除这一行将导致程序中其他地方的堆栈溢出。
这段代码实际上是在为BeforeAssemblyLoad事件安装一个处理程序,并尝试执行该处理程序
if (assemblyfilename.EndsWith("someparticular.dll", StringComparison.InvariantCultureIgnoreCase))
{
assemblyfilename = "someparticular_modified.dll";
}
到现在我都不用告诉你了。在字符串比较中使用从未使用过的区域性会导致程序集负载。InvariantCulture也不例外。
下面是一个示例,说明如何创建导致错误消息“试图读写受保护内存”的结构。这通常表明其他记忆被破坏了。”
成功与失败的差别非常细微。
下面的单元测试演示了这个问题。
看看你能不能找出问题出在哪里。
[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);
}
}