C#:使用不安全代码将变量与 default(T) 进行比较

C#: Use unsafe code to compare a variable to default(T)

提问人: 提问时间:6/17/2017 更新时间:6/23/2017 访问量:197

问:

我想测试未知类型的变量是否已被同化为非值。default

该变量可能是结构类型,因此我无法用 .where T : class

结构的实现通常会假设其字段已经分配给,因此我不能使用 ,否则它将崩溃并出现空指针异常。IEquatable<T>EqualityComparer<T>.default

(是的,我小心翼翼地确保结构的 0 值永远不会被视为有效值,因此我确信我可以专门处理它。

我愿意打开来实现这一点。我希望能够将正文写入此函数:/unsafe

unsafe static bool UnsafeIsDefault<T>(T a) {
    // Error: Operator '==' cannot be applied to operands of type 'T' and 'T'
    // return a == default(T);

    // Real body goes here
}

我意识到另一种解决方案是限制 ,并为我打算在此处使用的每种类型编写一个实现,但我希望避免这种情况。where T : ICheckForDefaultinterface ICheckForDefault { bool IsDefault(); }

C# .NET Equality 不安全 IEQUALITYCOMPARER

评论

1赞 stelioslogothetis 6/17/2017
有什么问题吗?object.Equals(value, default(T))
0赞 M.kazem Akhgary 6/18/2017
使用 you 可以做到 其中 T 是结构的类型IEquatable<T>myStruct.Equals(default(T));
0赞 6/18/2017
default(T)不一定是结构体的安全值,因此调用其实现将崩溃。我真的只想在这里进行位比较,并在其他地方进行逻辑比较。Equals
0赞 Evk 6/18/2017
您可以使用 object。Equals(value, default(T)),如上所述。它不会调用你的 struct Equals 方法,即使你重写了它或实现了 IEquatable。因此,即使您有一些结构在其相等比较中不需要默认字段值 - 它也不会崩溃。
1赞 6/18/2017
@Evk我测试了它,似乎确实在调用一个覆盖的.文档还说“这意味着如果 objA 重写 Object.Equals(Object) 方法,则调用此重写。 @PetSerAI 那行得通!如果你提交它,我会接受它作为答案。但它是装箱结构的吗?object.EqualsEquals

答:

0赞 Mert Akcakaya 6/18/2017 #1

这个假设一个变量(类或结构)是 default(T) 或刚刚初始化且未更改字段/属性的变量(类或结构)是默认的。

 class Program
    {
        static void Main(string[] args)
        {
            var defaultStruct = default(MyStruct);
            PrintIsDefault(IsDefault(defaultStruct), nameof(defaultStruct));

            var defaultInitializedStruct = new MyStruct();
            PrintIsDefault(IsDefault(defaultInitializedStruct), nameof(defaultInitializedStruct));

            var nonDefaultStruct = new MyStruct { Field1 = 13 };
            PrintIsDefault(IsDefault(nonDefaultStruct), nameof(nonDefaultStruct));

            var defaultChar = default(char);
            PrintIsDefault(IsDefault(defaultChar), nameof(defaultChar));

            var nonDefaultChar = 'a';
            PrintIsDefault(IsDefault(nonDefaultChar), nameof(nonDefaultChar));

            var defaultObject = default(object);
            PrintIsDefault(IsDefault(defaultObject), nameof(defaultObject));

            var nonDefaultObject = "string";
            PrintIsDefault(IsDefault(nonDefaultObject), nameof(nonDefaultObject));

            var defaultClass = default(MyClass);
            PrintIsDefault(IsDefault(defaultClass), nameof(defaultClass));

            var defaultInitializedClass = default(MyClass);
            PrintIsDefault(IsDefault(defaultInitializedClass), nameof(defaultInitializedClass));

            var nonDefaultClass = new MyClass { Field1 = 1, Prop1 = 2 };
            PrintIsDefault(IsDefault(nonDefaultClass), nameof(nonDefaultClass));

            Console.ReadLine();
        }

        private static bool IsDefault<T>(T value)
        {
            var typeInfo = typeof(T).GetTypeInfo();

            if (typeInfo.IsClass)
            {
                if (typeInfo.IsPrimitive || value is string || value is object)
                {
                    return Equals(value, default(T));
                }
                else
                {
                    return Equals(value, default(T)) ? true : AreMembersDefault(value);
                }
            }
            else
            {
                return typeInfo.IsPrimitive ? Equals(value, default(T)) : AreMembersDefault(value);
            }
        }

        private static bool AreMembersDefault<T>(T value)
        {
            var fields = value.GetType().GetFields();
            foreach (var field in fields)
            {
                if (!IsDefault((dynamic)(field.GetValue(value))))
                {
                    return false;
                }
            }

            var properties = value.GetType().GetProperties();
            foreach (var prop in properties)
            {
                if (!IsDefault((dynamic)(prop.GetValue(value))))
                {
                    return false;
                }
            }

            return true;
        }

        private static void PrintIsDefault(bool isDefault, string varName)
        {
            Console.WriteLine($"{varName} is default: {isDefault}");
        }
    }

输出:

defaultStruct is default: True
defaultInitializedStruct is default: True
nonDefaultStruct is default: False
defaultChar is default: True
nonDefaultChar is default: False
defaultObject is default: True
nonDefaultObject is default: False
defaultClass is default: True
defaultInitializedClass is default: True
nonDefaultClass is default: False
0赞 user6629955 6/23/2017 #2

还有一种不同类型的比较更合适,称为 .Equals

请尝试以下代码:

System.Runtime.CompilerServices.RuntimeHelpers.Equals(value, default(T))