我收集了一些极端案例和脑筋急转弯,总是想听到更多。这个页面只涵盖了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上尝试过……)
只是为了澄清,这只是我正在寻找的事情的一个例子-我并不是特别寻找对这个奇怪现象的讨论/解释。(这和普通的弦乐实习不一样;特别地,当调用构造函数时,字符串实习通常不会发生。)我真的是在要求类似的奇怪行为。
还有其他的宝藏吗?
有趣的是,当我第一次看到它时,我认为这是c#编译器正在检查的东西,但即使你直接发出IL来消除任何干扰的机会,它仍然会发生,这意味着它真的是newobj操作代码正在进行检查。
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);
如果检查string,它也等于true。这意味着这个操作代码必须有特殊的行为来实习空字符串。
我想我之前向您展示过这个,但我喜欢这里的乐趣——这需要一些调试才能跟踪!(原来的代码显然更加复杂和微妙……)
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());
}
那么T是什么?
答:任何可空<T> -如int?。所有的方法都被重写,除了GetType()不能;因此它被强制转换为object(因此为null)来调用object. gettype()…哪个调用null;-p
更新:情节变得越来越复杂……Ayende Rahien在他的博客上提出了类似的挑战,但使用了where 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?");
}
但它是可以被打败的!使用与远程处理一样的间接方式;警告-以下是纯粹的邪恶:
class MyFunnyProxyAttribute : ProxyAttribute {
public override MarshalByRefObject CreateInstance(Type serverType) {
return null;
}
}
[MyFunnyProxy]
class MyFunnyType : ContextBoundObject { }
在此之后,new()调用被重定向到代理(MyFunnyProxyAttribute),该代理返回null。现在去洗眼睛!
即使枚举函数重载,它们也应该使0为整数。
我知道c#核心团队将0映射到enum的基本原理,但是,它仍然没有像它应该的那样正交。来自Npgsql的例子。
测试的例子:
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");
}
}
}