What's the strangest corner case you've seen in C# or .NET?
我收集了一些角落的案例和脑筋急转弯,并总是想听到更多。这个页面只涵盖了C语言和BOB,但我也发现核心.NET内容也很有趣。例如,这里有一个不在页面上,但我觉得难以置信:
1 2 3 |
我希望打印错误——毕竟,"新建"(带有引用类型)总是创建一个新对象,不是吗?C和CLI的规范都表明应该这样做。好吧,在这种特殊情况下不是。它打印的是真的,并且在我测试过的框架的每个版本上都做过。(诚然,我还没试过单声道……)
只是说清楚,这只是我在寻找的一个例子-我不是特别想讨论/解释这个奇怪的事情。(这与普通的字符串实习生不同,尤其是,当调用构造函数时,字符串实习生通常不会发生。)我真的要求类似的奇怪行为。
还有其他的宝石吗?
我想我以前给你看过这个,但我喜欢这里的乐趣-这需要一些调试来跟踪!(原始代码显然更复杂和微妙…)
1 2 3 4 5 6 7 8 9 10 11 12 | 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()); } |
所以什么是…
答:任何
更新:情节变厚…Ayende Rahien在他的博客中也提出了类似的挑战,但是他用了一个
1 2 3 4 5 6 7 8 |
但它可以被打败!使用与远程处理相同的间接方法;警告-以下是纯粹的邪恶:
1 2 3 4 5 6 7 | class MyFunnyProxyAttribute : ProxyAttribute { public override MarshalByRefObject CreateInstance(Type serverType) { return null; } } [MyFunnyProxy] class MyFunnyType : ContextBoundObject { } |
这样,
银行家的四舍五入。
这一个不是编译器的错误或故障,而是一个奇怪的角落案例…
.NET框架采用了一种称为银行家舍入的方案或舍入。
在银行家的四舍五入中,0.5的数字四舍五入到最接近的偶数,因此
1 2 3 4 5 | Math.Round(-0.5) == 0 Math.Round(0.5) == 0 Math.Round(1.5) == 2 Math.Round(2.5) == 2 etc... |
这可能会导致基于更著名的四舍五入的财务计算中出现一些意想不到的错误。
在Visual Basic中也是如此。
如果以
1 2 3 4 5 6 7 8 | static void Rec(int i) { Console.WriteLine(i); if (i < int.MaxValue) { Rec(i + 1); } } |
答:
- 在32位JIT上,它应导致StackOverflowException
- 在64位JIT上,它应该将所有数字打印到int.maxvalue
这是因为64位JIT编译器应用了尾调用优化,而32位JIT则没有。
不幸的是,我还没有一台64位机器来验证这一点,但该方法确实满足尾调用优化的所有条件。如果有人有,我会有兴趣看看这是不是真的。
分配这个!
这是我在聚会上喜欢问的问题(这可能是我不再被邀请的原因):
你能编译下面的代码吗?
1 2 3 4 |
一个简单的欺骗可能是:
1 2 3 4 5 6 | string cheat = @" public void Foo() { this = new Teaser(); } "; |
但真正的解决办法是:
1 2 3 4 5 6 7 |
因此,值类型(结构)可以重新分配它们的
几年前,在致力于忠诚度计划时,我们对给客户的点数有一个问题。这个问题与铸造/转换double到int有关。
在下面的代码中:
1 2 3 4 | double d = 13.6; int i1 = Convert.ToInt32(d); int i2 = (int)d; |
I1=I2吗?
原来是I1!= I2。由于convert和cast运算符中的舍入策略不同,实际值为:
1 2 | i1 == 14 i2 == 13 |
最好是调用math.heiling()或math.floor()(或math.round,中间点为rounding,满足我们的要求)
1 2 | int i1 = Convert.ToInt32( Math.Ceiling(d) ); int i2 = (int) Math.Ceiling(d); |
即使存在枚举函数重载,它们也应该使0成为整数。
我知道C核心团队将0映射到枚举的基本原理,但它仍然没有它应该的那样正交。来自NPGSQL的示例。
测试实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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"); } } } |
这是迄今为止我看到的最不寻常的一个(当然,除了这里的那些!):
1 2 3 | public class Turtle<T> where T : Turtle<T> { } |
它可以让你申报,但没有真正的用途,因为它总是要求你用另一只乌龟来包装你在中心的任何课程。
[开玩笑]我想是乌龟一直往下…[笑话]
这是我最近才发现的一个…
1 2 3 4 5 6 7 | interface IFoo { string Message {get;} } ... IFoo obj = new IFoo("abc"); Console.WriteLine(obj.Message); |
乍一看,上面看起来很疯狂,但实际上是合法的。不,真的(虽然我遗漏了一个关键部分,但它并不像"添加一个名为
看看你是否能找出原因:谁说你不能实例化一个接口?
什么时候布尔值既不是真也不是假?
比尔发现你可以破解一个布尔值,所以如果A是真的,B是真的,(A和B)是假的。
黑客攻击的布尔人
我来派对有点晚了,但是我有three-four->strike>five:
如果对一个尚未加载/显示的控件轮询InvokereRequired,如果试图从另一个线程更改该控件,它会说false,并在您的脸上爆炸(解决方案是引用该控件的创建者中的this.handle)。
另一个绊倒我的是给了我一个装配:
1 2 3 4 5 | enum MyEnum { Red, Blue, } |
如果在另一个程序集中计算myEnum.red.ToString(),并且在两次之间有人将枚举重新编译为:
1 2 3 4 5 6 | enum MyEnum { Black, Red, Blue, } |
在运行时,您将得到"黑色"。
我有一个共享的程序集,里面有一些方便的常量。我的前任留下了一大堆难看的"只获取"属性,我想我可以摆脱混乱,只使用公共常量。当vs将它们编译为它们的值而不是引用时,我感到非常惊讶。
如果从另一个程序集实现接口的新方法,但引用该程序集的旧版本重新生成,则会得到typeloadException(不实现"new method"),即使您已经实现了它(请参见此处)。
dictionary<,>:"返回项目的顺序未定义"。这太可怕了,因为它有时会咬你,但也会伤害其他人,如果你只是盲目地认为字典会很好用("为什么不应该呢?"我想,列表的确如此),在你最终开始质疑你的假设之前,你真的必须仔细研究它。
vb.net、nullables和三元运算符:
1 | Dim i As Integer? = If(True, Nothing, 5) |
我花了一些时间来调试,因为我期望
我真正包含什么?
这是令人惊讶的,但实际上是"正确"的行为:vb.net中的
我发现了第二个非常奇怪的街角案件,比我的第一个案件有了长足的进步。
equals方法(string、string、stringcomparison)实际上不是没有副作用的。
我正在开发一个代码块,它在某个函数的顶部有一行代码:
1 | stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase); |
删除该行会导致程序中其他地方的堆栈溢出。
事实证明,代码正在为本质上是beforeassemblyload事件的事件安装处理程序,并尝试执行此操作。
1 2 3 4 | if (assemblyfilename.EndsWith("someparticular.dll", StringComparison.InvariantCultureIgnoreCase)) { assemblyfilename ="someparticular_modified.dll"; } |
现在我不该告诉你。在字符串比较中使用以前未使用的区域性会导致程序集加载。InvariantCulture也不例外。
下面是一个示例,说明如何创建导致错误消息"尝试读取或写入受保护的内存"的结构。这通常表示其他内存已损坏"。成功和失败之间的区别是非常微妙的。
下面的单元测试演示了这个问题。
看看你能不能找出出什么问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | [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); } } |
C支持数组和列表之间的转换,只要数组不是多维的,并且类型之间存在继承关系,类型是引用类型。
1 2 3 4 5 6 |
请注意,这不起作用:
1 2 | 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[]' |
这是我偶然遇到的最奇怪的事情:
1 2 3 4 5 6 7 | public class DummyObject { public override string ToString() { return null; } } |
使用方法如下:
1 2 |
会抛出一个
1 2 3 4 | 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条规定的一个缺陷:
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.
有趣的是,当我第一次看到它时,我假设它是C编译器正在检查的,但是即使您直接发出IL以消除任何干扰的可能性,它仍然会发生,这意味着它确实是执行检查的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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); |
如果您对照
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |
输出"尝试读取受保护的内存。这表示其他内存已损坏。"
今天刚发现一个好东西:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class Base { public virtual void Initialize(dynamic stuff) { //... } } public class Derived:Base { public override void Initialize(dynamic stuff) { base.Initialize(stuff); //... } } |
这会引发编译错误。
对方法"initialize"的调用需要动态调度,但不能是,因为它是基本访问表达式的一部分。考虑强制转换动态参数或消除基本访问。
如果我写base.initialize(作为对象的东西);它工作得很好,但是这似乎是一个"神奇的词",因为它做的完全相同,所以所有的东西仍然是动态的…
setValue()可以将Ints赋给枚举,Ints赋给可为空的Ints,Enum赋给可为空的Enum,但Ints不赋给可为空的Enum。
1 2 3 4 | 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 !!! |
此处完整描述
C无障碍拼图
以下派生类正在从其基类访问私有字段,编译器将静默地查看另一面:
1 2 3 4 5 6 7 | public class Derived : Base { public int BrokenAccess() { return base.m_basePrivateField; } } |
这个领域确实是私有的:
1 | private int m_basePrivateField = 0; |
想知道我们怎样才能编译这样的代码吗?
.
.
.
.
.
.
.
回答诀窍是宣布
1 2 3 4 5 6 7 8 9 10 11 12 | public class Base { private int m_basePrivateField = 0; public class Derived : Base { public int BrokenAccess() { return base.m_basePrivateField; } } } |
内部类可以完全访问外部类成员。在这种情况下,内部类也恰好派生自外部类。这允许我们"打破"私有成员的封装。
如果您有一个泛型类,该类的方法可能因类型参数而变得不明确,该怎么办?我最近在写一本双向字典时遇到了这种情况。我想写对称的
1 2 3 4 5 | class TwoWayRelationship<T1, T2> { public T2 Get(T1 key) { /* ... */ } public T1 Get(T2 key) { /* ... */ } } |
如果你举一个例子,其中
1 2 3 |
但是如果
1 2 |
有趣的是,第二种情况下的所有其他方法仍然可用;它只是对现在不明确的方法的调用,这会导致编译器错误。有趣的情况,如果有点不太可能和模糊。
在我们使用的API中,返回域对象的方法可能会返回一个特殊的"空对象"。在此实现中,如果比较
所以这个API的用户可能有如下代码:
1 | return test != null ? test : GetDefault(); |
或者更详细一点,比如:
1 2 3 | if (test == null) return GetDefault(); return test; |
其中,
1 | return test ?? GetDefault(); |
如果测试对象是从API返回的空对象,而不是正确的
想想这个奇怪的案例:
1 2 3 4 5 6 7 | public interface MyInterface { void Method(); } public class Base { public void Method() { } } public class Derived : Base, MyInterface { } |
如果
如果
编译器必须这样做,以支持接口的多态调度,也就是说,它必须使该方法成为虚拟的。
这个很难顶。我在尝试构建真正支持begin/endinvoke的realproxy实现时遇到了这个问题(感谢MS让没有可怕的黑客就无法做到这一点)。此示例基本上是CLR中的一个bug,BeginInvoke的非托管代码路径不验证来自realProxy.privateInvoke(和my invoke override)的返回消息是否返回IAsyncResult的实例。当它被返回时,clr会变得非常混乱,并且对正在发生的事情一无所知,正如底部的测试所证明的那样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | 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. } } } |
输出:
1 2 3 4 5 6 7 | 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) |
以下可能是我所缺乏的常识,但是。不久前,我们有一个包含虚拟属性的bug案例。将上下文抽象一点,考虑以下代码,并将断点应用于指定区域:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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 { } } } |
在
我花了些时间才意识到发生了什么。最后我被快速表给启发了。当进入QuickWatch并浏览
1 | ((TestProject1.Base)(d)) |
也就是说,如果这样替换base,那么调用
1 | public string BaseProperty { get { return ((TestProject1.Base)(d)).Property; } private set { } } |
对于手表,QuickWatch和调试鼠标在工具提示上,然后在考虑多态性时显示
不管怎样,这显然不会改变任何功能,
"Oi, wait..what ? omg that DLL is
like, ..doing something funny"
您有没有想过C编译器会生成无效的CIL?运行这个,你会得到一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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)); } } |
不过,我不知道在C 4.0编译器中它是如何运行的。
编辑:这是我的系统的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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] |
我不确定你会说这是Windows Vista/7的怪胎还是.NET的怪胎,但它让我抓耳挠腮了一会儿。
1 2 3 4 | 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." |
在Windows Vista/7中,文件将实际写入
C有一些非常令人兴奋的地方,它处理闭包的方式。
它不是将堆栈变量值复制到无闭包变量,而是将变量的所有出现都打包到一个对象中,从而将其移出堆栈-直接移到堆中!:)
我猜,这使得C语言在功能上比ML本身(使用堆栈值复制afaik)更加完整(或者lambda完成huh))。F也有这个特性,就像C一样。
这确实给我带来了很多快乐,谢谢你们,伙计们!
不过,这不是一件怪事,也不是一件角落里的案子…但是,基于堆栈的虚拟机语言真的出乎意料:)
从我不久前问的一个问题来看:
条件运算符不能隐式强制转换?
鉴于:
1 | Bool aBoolValue; |
如果指定
以下内容将无法编译:
1 | Byte aByteValue = aBoolValue ? 1 : 0; |
但这会:
1 | Int anIntValue = aBoolValue ? 1 : 0; |
提供的答案也很好。
以下内容不起作用:
1 2 3 4 | if (something) doit(); else var v = 1 + 2; |
但这是可行的:
1 2 3 4 5 | if (something) doit(); else { var v = 1 + 2; } |
C中的范围有时确实很奇怪。让我举一个例子:
1 2 3 4 5 6 | if (true) { OleDbCommand command = SQLServer.CreateCommand(); } OleDbCommand command = SQLServer.CreateCommand(); |
编译失败,因为命令被重新声明?关于它为什么在stackoverflow的这个线程和我的博客中这样工作,有一些有趣的猜测。
以下是我的一些:
先简单一点:零点{号码=1}
1 2 3 4 5 | public bool ReturnsFalse() { //The default value is not defined! return Enum.IsDefined(typeof (NoZero), default(NoZero)); } |
下面的代码实际上可以打印为真!
1 2 3 4 5 6 7 | internal sealed class Strange { public void Foo() { Console.WriteLine(this == null); } } |
一个简单的客户端代码,它将导致代表空虚的地狱之门(奇怪的酒吧);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class Program { [STAThread()] public static void Main(string[] args) { Strange bar = null; var hello = new DynamicMethod("ThisIsNull", typeof(void), new[] { typeof(Strange) }, typeof(Strange).Module); ILGenerator il = hello.GetILGenerator(256); il.Emit(OpCodes.Ldarg_0); var foo = typeof(Strange).GetMethod("Foo"); il.Emit(OpCodes.Call, foo); il.Emit(OpCodes.Ret); var print = (HelloDelegate)hello.CreateDelegate(typeof(HelloDelegate)); print(bar); Console.ReadLine(); } } |
在大多数语言中,只要实例方法被调用时不使用对象的状态,这实际上是正确的。只有在访问对象的状态时才会取消引用。
这个很简单,但我还是觉得有点有趣。打给foo后x的值是多少?
1 2 3 4 5 6 7 8 9 | static int x = 0; public static void Foo() { try { return; } finally { x = 1; } } static void Main() { Foo(); } |
如果您有扩展方法:
1 2 3 4 5 6 7 8 9 | public static bool? ToBoolean(this string s) { bool result; if (bool.TryParse(s, out result)) return result; else return null; } |
这个代码:
1 2 | string nullStr = null; var res = nullStr.ToBoolean(); |
这不会引发异常,因为它是一个扩展方法(实际上是
这一次我真的很困惑(我为长度道歉,但它是winform)。不久前我把它发到了新闻组。
I've come across an interesting bug. I
have workarounds but i'd like to know
the root of the problem. I've stripped
it down into a short file and hope
someone might have an idea about
what's going on.It's a simple program that loads a
control onto a form and binds"Foo"
against a combobox ("SelectedItem")
for it's"Bar" property and a
datetimepicker ("Value") for it's
"DateTime" property. The
DateTimePicker.Visible value is set to
false. Once it's loaded up, select the
combobox and then attempt to deselect
it by selecting the checkbox. This is
rendered impossible by the combobox
retaining the focus, you cannot even
close the form, such is it's grasp on
the focus.I have found three ways of fixing this
problem.a) Remove the binding to Bar (a bit
obvious)b) Remove the binding to
DateTimec) Make the DateTimePicker
visible !?!I'm currently running Win2k. And .NET
2.00, I think 1.1 has the same problem. Code is below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | using System; using System.Collections; using System.Windows.Forms; namespace WindowsApplication6 { public class Bar { public Bar() { } } public class Foo { private Bar m_Bar = new Bar(); private DateTime m_DateTime = DateTime.Now; public Foo() { } public Bar Bar { get { return m_Bar; } set { m_Bar = value; } } public DateTime DateTime { get { return m_DateTime; } set { m_DateTime = value; } } } public class TestBugControl : UserControl { public TestBugControl() { InitializeComponent(); } public void InitializeData(IList types) { this.cBoxType.DataSource = types; } public void BindFoo(Foo foo) { this.cBoxType.DataBindings.Add("SelectedItem", foo,"Bar"); this.dtStart.DataBindings.Add("Value", foo,"DateTime"); } /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.checkBox1 = new System.Windows.Forms.CheckBox(); this.cBoxType = new System.Windows.Forms.ComboBox(); this.dtStart = new System.Windows.Forms.DateTimePicker(); this.SuspendLayout(); // // checkBox1 // this.checkBox1.AutoSize = true; this.checkBox1.Location = new System.Drawing.Point(14, 5); this.checkBox1.Name ="checkBox1"; this.checkBox1.Size = new System.Drawing.Size(97, 20); this.checkBox1.TabIndex = 0; this.checkBox1.Text ="checkBox1"; this.checkBox1.UseVisualStyleBackColor = true; // // cBoxType // this.cBoxType.FormattingEnabled = true; this.cBoxType.Location = new System.Drawing.Point(117, 3); this.cBoxType.Name ="cBoxType"; this.cBoxType.Size = new System.Drawing.Size(165, 24); this.cBoxType.TabIndex = 1; // // dtStart // this.dtStart.Location = new System.Drawing.Point(117, 40); this.dtStart.Name ="dtStart"; this.dtStart.Size = new System.Drawing.Size(165, 23); this.dtStart.TabIndex = 2; this.dtStart.Visible = false; // // TestBugControl // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.dtStart); this.Controls.Add(this.cBoxType); this.Controls.Add(this.checkBox1); this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Margin = new System.Windows.Forms.Padding(4); this.Name ="TestBugControl"; this.Size = new System.Drawing.Size(285, 66); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.CheckBox checkBox1; private System.Windows.Forms.ComboBox cBoxType; private System.Windows.Forms.DateTimePicker dtStart; } public class Form1 : Form { public Form1() { InitializeComponent(); this.Load += new EventHandler(Form1_Load); } void Form1_Load(object sender, EventArgs e) { InitializeControl(); } public void InitializeControl() { TestBugControl control = new TestBugControl(); IList list = new ArrayList(); for (int i = 0; i < 10; i++) { list.Add(new Bar()); } control.InitializeData(list); control.BindFoo(new Foo()); this.Controls.Add(control); } /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Text ="Form1"; } #endregion } static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } } |
以下打印错误,而不是引发溢出异常:
1 2 3 4 5 6 7 | Console.WriteLine("{0}", yep(int.MaxValue )); private bool yep( int val ) { return ( 0 < val * 2); } |
我认为这个问题的答案是因为.NET使用字符串分隔可能导致相等字符串指向同一对象的内容(因为字符串是可变的,所以这不是问题)
(我不是在谈论字符串类上重写的相等运算符)