我收集了一些极端案例和脑筋急转弯,总是想听到更多。这个页面只涵盖了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#中的作用域有时真的很奇怪。让我给你们举个例子:

if (true)
{
   OleDbCommand command = SQLServer.CreateCommand();
}

OleDbCommand command = SQLServer.CreateCommand();

编译失败,因为命令被重新声明?在stackoverflow的这个帖子和我的博客中,有一些有趣的猜测,为什么它会这样工作。

其他回答

如果您有一个泛型类,它的方法可以根据类型参数而变得模糊,该怎么办?我最近在写一本双向词典时遇到了这种情况。我想要编写对称的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..."

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

VB。NET、可空值和三元操作符:

Dim i As Integer? = If(True, Nothing, 5)

这花了我一些时间来调试,因为我希望I包含Nothing。

i到底包含了什么?0.

这是令人惊讶的,但实际上是“正确的”行为:在VB中没有。NET与CLR中的null并不完全相同:对于值类型T,没有值可以表示null或默认值(T),这取决于上下文。在上面的例子中,If将Integer推断为Nothing和5的常见类型,因此,在这种情况下,Nothing意味着0。

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

输出为“试图读取受保护的内存。”这表明其他记忆已经被破坏了。”

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

如果你有扩展方法:

public static bool? ToBoolean(this string s)
{
    bool result;

    if (bool.TryParse(s, out result))
        return result;
    else
        return null;
}

这段代码:

string nullStr = null;
var res = nullStr.ToBoolean();

这不会抛出异常,因为它是一个扩展方法(实际上是HelperClass.ToBoolean(null)),而不是一个实例方法。这可能会令人困惑。