您在 C# 或 .NET 中见过的最奇怪的极端情况是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/194484/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:32:04  来源:igfitidea点击:

What's the strangest corner case you've seen in C# or .NET?

c#.net

提问by Jon Skeet

I collect a few corner cases and brain teasersand would always like to hear more. The page only really covers C# language bits and bobs, but I also find core .NET things interesting too. For example, here's one which isn't on the page, but which I find incredible:

我收集了一些角落案例和脑筋急转弯,并且总是希望听到更多。该页面只真正涵盖了 C# 语言的一些细节,但我也发现核心 .NET 的东西也很有趣。例如,这里有一个不在页面上,但我觉得不可思议的:

string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x, y));

I'd expect that to print False - after all, "new" (with a reference type) alwayscreates a new object, doesn't it? The specs for both C# and the CLI indicate that it should. Well, not in this particular case. It prints True, and has done on every version of the framework I've tested it with. (I haven't tried it on Mono, admittedly...)

我希望打印 False - 毕竟,“new”(带有引用类型)总是创建一个新对象,不是吗?C# 和 CLI 的规范都表明它应该。好吧,不是在这种特殊情况下。它打印 True,并且在我测试过的框架的每个版本上都完成了。(无可否认,我还没有在 Mono 上尝试过...)

Just to be clear, this is only an example of the kind of thing I'm looking for - I wasn't particularly looking for discussion/explanation of this oddity. (It's not the same as normal string interning; in particular, string interning doesn't normally happen when a constructor is called.) I was really asking for similar odd behaviour.

需要明确的是,这只是我正在寻找的那种东西的一个例子——我并不是特别在寻找对这种奇怪现象的讨论/解释。(这与普通的字符串实习不同;特别是,在调用构造函数时通常不会发生字符串实习。)我真的要求类似的奇怪行为。

Any other gems lurking out there?

还有其他宝石潜伏在那里吗?

采纳答案by Marc Gravell

I think I showed you this one before, but I like the fun here - this took some debugging to track down! (the original code was obviously more complex and subtle...)

我想我之前给你看过这个,但我喜欢这里的乐趣 - 这需要一些调试才能找到!(原代码显然更加复杂和微妙......)

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

So what was T...

那么什么是T...

Answer: any Nullable<T>- such as int?. All the methods are overridden, except GetType() which can't be; so it is cast (boxed) to object (and hence to null) to call object.GetType()... which calls on null ;-p

答案:任何Nullable<T>- 例如int?. 所有方法都被覆盖,除了 GetType() 不能被覆盖;所以它被转换(装箱)到对象(并因此为空)来调用 object.GetType()... 调用 null ;-p



Update: the plot thickens... Ayende Rahien threw down a similar challenge on his blog, but with a where T : class, new():

更新:情节变厚了...... 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?");
}

But it can be defeated! Using the same indirection used by things like remoting; warning - the following is pure evil:

但它可以被打败!使用与远程处理相同的间接方式;警告 - 以下是纯粹的邪恶

class MyFunnyProxyAttribute : ProxyAttribute {
    public override MarshalByRefObject CreateInstance(Type serverType) {
        return null;
    }
}
[MyFunnyProxy]
class MyFunnyType : ContextBoundObject { }

With this in place, the new()call is redirected to the proxy (MyFunnyProxyAttribute), which returns null. Now go and wash your eyes!

有了这个,new()调用被重定向到代理 ( MyFunnyProxyAttribute),它返回null. 现在去洗你的眼睛!

回答by Joshua

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

The output is "Attempted to read protected memory. This is an indication that other memory is corrupt."

输出是“尝试读取受保护的内存。这表明其他内存已损坏。”

回答by Samuel Kim

Bankers' Rounding.

银行家四舍五入。

This one is not so much a compiler bug or malfunction, but certainly a strange corner case...

这与其说是编译器错误或故障,不如说是一个奇怪的极端情况……

The .Net Framework employs a scheme or rounding known as Banker's Rounding.

.Net 框架采用称为银行家舍入的方案或舍入。

In Bankers' Rounding the 0.5 numbers are rounded to the nearest even number, so

在银行家的四舍五入中,0.5 数字四舍五入到最接近的偶数,所以

Math.Round(-0.5) == 0
Math.Round(0.5) == 0
Math.Round(1.5) == 2
Math.Round(2.5) == 2
etc...

This can lead to some unexpected bugs in financial calculations based on the more well known Round-Half-Up rounding.

这可能会导致基于更广为人知的 Round-Half-Up 四舍五入的财务计算中出现一些意外错误。

This is also true of Visual Basic.

Visual Basic 也是如此。

回答by James Z

I think the answer to the question is because .net uses string interning something that might cause equal strings to point to the same object (since a strings are mutable this is not a problem)

我认为这个问题的答案是因为 .net 使用字符串实习可能导致相等的字符串指向同一个对象(因为字符串是可变的,这不是问题)

(I'm not talking about the overridden equality operator on the string class)

(我不是在谈论字符串类上的覆盖相等运算符)

回答by Greg Beech

Interesting - when I first looked at that I assumed it was something the C# compiler was checking for, but even if you emit the IL directly to remove any chance of interference it still happens, which means it really is the newobjop-code that's doing the checking.

有趣 - 当我第一次看到它时,我认为它是 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);

It also equates to trueif you check against string.Emptywhich means this op-code must have special behaviour to intern empty strings.

它还等同于true如果您检查string.Empty这意味着此操作码必须具有特殊行为来实习空字符串。

回答by Jonathan Allen

When is a Boolean neither True nor False?

什么时候布尔值既不是 True 也不是 False?

Bill discovered that you can hack a boolean so that if A is True and B is True, (A and B) is False.

比尔发现你可以破解一个布尔值,这样如果 A 为真,B 为真,(A 和 B)为假。

Hacked Booleans

被黑的布尔值

回答by Greg Beech

What will this function do if called as Rec(0)(not under the debugger)?

如果被调用Rec(0)(不在调试器下),这个函数会做什么?

static void Rec(int i)
{
    Console.WriteLine(i);
    if (i < int.MaxValue)
    {
        Rec(i + 1);
    }
}

Answer:

回答:

  • On 32-bit JIT it should result in a StackOverflowException
  • On 64-bit JIT it should print all the numbers to int.MaxValue
  • 在 32 位 JIT 上,它应该导致 StackOverflowException
  • 在 64 位 JIT 上,它应该将所有数字打印到 int.MaxValue

This is because the 64-bit JIT compiler applies tail call optimisation, whereas the 32-bit JIT does not.

这是因为64 位 JIT 编译器应用了尾调用优化,而 32 位 JIT 则没有。

Unfortunately I haven't got a 64-bit machine to hand to verify this, but the method does meet all the conditions for tail-call optimisation. If anybody does have one I'd be interested to see if it's true.

不幸的是,我手头没有 64 位机器来验证这一点,但该方法确实满足尾调用优化的所有条件。如果有人确实有一个,我很想看看它是否属实。

回答by Craig Eddy

The following prints False instead of throwing an overflow exception:

以下打印 False 而不是引发溢出异常:

Console.WriteLine("{0}", yep(int.MaxValue ));


private bool yep( int val )
{
    return ( 0 < val * 2);
}

回答by Quibblesome

This one had me truly puzzled (I apologise for the length but it's WinForm). I posted it in the newsgroupsa while back.

这个让我真的很困惑(我很抱歉它的长度,但它是 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.

我遇到了一个有趣的错误。我有解决方法,但我想知道问题的根源。我已经把它分解成一个简短的文件,希望有人能知道发生了什么。

这是一个简单的程序,它将控件加载到窗体上,并将“Foo”绑定到组合框(“SelectedItem”)的“Bar”属性和日期时间选择器(“Value”)的“DateTime”属性。DateTimePicker.Visible 值设置为 false。加载后,选择组合框,然后尝试通过选中复选框来取消选择它。组合框保持焦点使这变得不可能,您甚至无法关闭表单,例如它对焦点的掌握。

我找到了解决这个问题的三种方法。

a) 去掉对 Bar 的绑定(有点明显)

b) 移除对 DateTime 的绑定

c) 使 DateTimePicker 可见!?!

我目前正在运行 Win2k。和 .NET 2.00,我认为 1.1 也有同样的问题。代码如下。

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

回答by Peter van der Heijden

C# supports conversions between arrays and lists as long as the arrays are not multidimensional and there is an inheritance relation between the types and the types are reference types

C#支持数组和列表之间的转换,只要数组不是多维的,类型之间有继承关系,类型是引用类型

object[] oArray = new string[] { "one", "two", "three" };
string[] sArray = (string[])oArray;

// Also works for IList (and IEnumerable, ICollection)
IList<string> sList = (IList<string>)oArray;
IList<object> oList = new string[] { "one", "two", "three" };

Note that this does not work:

请注意,这不起作用:

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[]'