我收集了一些极端案例和脑筋急转弯,总是想听到更多。这个页面只涵盖了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上尝试过……)
只是为了澄清,这只是我正在寻找的事情的一个例子-我并不是特别寻找对这个奇怪现象的讨论/解释。(这和普通的弦乐实习不一样;特别地,当调用构造函数时,字符串实习通常不会发生。)我真的是在要求类似的奇怪行为。
还有其他的宝藏吗?
如果您有一个泛型类,它的方法可以根据类型参数而变得模糊,该怎么办?我最近在写一本双向词典时遇到了这种情况。我想要编写对称的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..."
有趣的是,第二种情况下的所有其他方法仍然可用;只有调用现在模棱两可的方法才会导致编译器错误。有趣的案例,只是有点不太可能和晦涩。
我不确定你是否会说这是Windows Vista/7的怪癖或。net的怪癖,但它让我挠头了好一阵子。
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:\Users\<username>\Virtual Store\Program Files\my folder\test.txt
这是我无意中遇到的最奇怪的事情:
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,则替换为空字符串。
This one's pretty hard to top. I ran into it while I was trying to build a RealProxy implementation that truly supports Begin/EndInvoke (thanks MS for making this impossible to do without horrible hacks). This example is basically a bug in the CLR, the unmanaged code path for BeginInvoke doesn't validate that the return message from RealProxy.PrivateInvoke (and my Invoke override) is returning an instance of an IAsyncResult. Once it's returned, the CLR gets incredibly confused and loses any idea of whats going on, as demonstrated by the tests at the bottom.
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.
}
}
}
输出:
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)
几年前,在制定忠诚度计划时,我们遇到了一个关于给予客户积分数量的问题。这个问题与将double类型转换为int类型有关。
代码如下:
double d = 13.6;
int i1 = Convert.ToInt32(d);
int i2 = (int)d;
i1 == i2吗?
结果是i1 != i2。
由于Convert和cast运算符的舍入策略不同,实际值为:
i1 == 14
i2 == 13
调用Math. ceiling()或Math. floor()(或Math. ceiling())总是更好。用符合我们要求的midpointrsurround进行四舍五入)
int i1 = Convert.ToInt32( Math.Ceiling(d) );
int i2 = (int) Math.Ceiling(d);