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

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

还有其他的宝藏吗?


当前回答

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) 

其他回答

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) 

这个让我很困惑(我很抱歉篇幅太长,但它是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 DateTime c) Make the DateTimePicker visible !?! I'm currently running Win2k. And .NET 2.00, I think 1.1 has the same problem. Code is below.

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

以下是我的一些建议:

当调用实例方法而不抛出NullReferenceException时,此值可以为null 不必为枚举定义默认枚举值

首先简单一点: enum NoZero { Number = 1 }

        public bool ReturnsFalse()
        {
            //The default value is not defined!
            return Enum.IsDefined(typeof (NoZero), default(NoZero));
        }

下面的代码实际上可以打印真!

 internal sealed class Strange
{
    public void Foo()
    {
        Console.WriteLine(this == null);
    }
}

一段简单的客户端代码将导致这样的结果 HelloDelegate(奇怪的条);

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

这在大多数语言中都是正确的,只要调用实例方法时不使用对象的状态。只有在访问对象的状态时才解除引用

下面是一个示例,说明如何创建导致错误消息“试图读写受保护内存”的结构。这通常表明其他记忆被破坏了。” 成功与失败的差别非常细微。

下面的单元测试演示了这个问题。

看看你能不能找出问题出在哪里。

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

我不确定你是否会说这是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