我收集了一些极端案例和脑筋急转弯,总是想听到更多。这个页面只涵盖了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上尝试过……)

只是为了澄清,这只是我正在寻找的事情的一个例子-我并不是特别寻找对这个奇怪现象的讨论/解释。(这和普通的弦乐实习不一样;特别地,当调用构造函数时,字符串实习通常不会发生。)我真的是在要求类似的奇怪行为。

还有其他的宝藏吗?


当前回答

我来派对晚了一点,但我有三四五

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: 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. 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). 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.

其他回答

我发现了第二个非常奇怪的极端情况,远远超过了我的第一个。

字符串。Equals Method (String, String, StringComparison)实际上并不是没有副作用的。

我当时在写一段代码,在某个函数的顶部有一行这样的代码:

stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase);

删除这一行将导致程序中其他地方的堆栈溢出。

这段代码实际上是在为BeforeAssemblyLoad事件安装一个处理程序,并尝试执行该处理程序

if (assemblyfilename.EndsWith("someparticular.dll", StringComparison.InvariantCultureIgnoreCase))
{
    assemblyfilename = "someparticular_modified.dll";
}

到现在我都不用告诉你了。在字符串比较中使用从未使用过的区域性会导致程序集负载。InvariantCulture也不例外。

我想我之前向您展示过这个,但我喜欢这里的乐趣——这需要一些调试才能跟踪!(原来的代码显然更加复杂和微妙……)

    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");
        }
    }
}

有趣的是,当我第一次看到它时,我认为这是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。这意味着这个操作代码必须有特殊的行为来实习空字符串。

如果您有一个泛型类,它的方法可以根据类型参数而变得模糊,该怎么办?我最近在写一本双向词典时遇到了这种情况。我想要编写对称的Get()方法,它将返回传递的任何参数的相反值。就像这样:

class TwoWayRelationship<T1, T2>
{
    public T2 Get(T1 key) { /* ... */ }
    public T1 Get(T2 key) { /* ... */ }
}

如果你创建一个实例,其中T1和T2是不同类型的,那么一切都很好:

var r1 = new TwoWayRelationship<int, string>();
r1.Get(1);
r1.Get("a");

但如果T1和T2是相同的(可能如果一个是另一个的子类),这是一个编译器错误:

var r2 = new TwoWayRelationship<int, int>();
r2.Get(1);  // "The call is ambiguous..."

有趣的是,第二种情况下的所有其他方法仍然可用;只有调用现在模棱两可的方法才会导致编译器错误。有趣的案例,只是有点不太可能和晦涩。