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

C#:使用不安全代码将变量与默认值(T)进行比较,c#,.net,equality,unsafe,iequalitycomparer,C#,.net,Equality,Unsafe,Iequalitycomparer,我想测试未知类型的变量是否被分配为非默认值 这个变量可能是一个结构类型,所以我不能用解决这个问题,其中t:class 结构的IEquatable实现通常会假定其字段已分配给,因此我不能使用EqualityComparer.default,否则它将因空指针异常而崩溃 (是的,我正在小心地确保结构的0值永远不会被视为有效值,因此我确信我可以特别对待它。) 我愿意打开/unsafe来完成这项任务。我希望能够将正文写入此函数: unsafe static bool UnsafeIsDefault<

我想测试未知类型的变量是否被分配为非默认值

这个变量可能是一个结构类型,所以我不能用
解决这个问题,其中t:class

结构的
IEquatable
实现通常会假定其字段已分配给,因此我不能使用
EqualityComparer.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
}
不安全静态bool UnsafeIsDefault(TA){
//错误:运算符“==”不能应用于“T”和“T”类型的操作数
//返回a==默认值(T);
//这里有真人
}

我意识到另一种解决方案是使用接口ICheckForDefault{bool IsDefault();}约束
其中的T:ICheckForDefault
,并为我打算在这里使用的每种类型编写一个实现,但我希望避免这种情况。

这一方案假设一个变量(类或结构)是默认值(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}");
        }
    }

这一个假设一个变量(类或结构)是默认的(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}");
        }
    }

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

请尝试以下代码:

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

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

请尝试以下代码:

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

object.Equals(value,default(T))
有什么问题吗?使用
IEquatable
可以执行
myStruct.Equals(default(T))
其中T是结构的类型
默认值(T)
不一定是结构的安全值,因此调用其
Equals
实现将崩溃。我真的只想在这里进行位比较,在其他地方进行逻辑比较。您可以按照上面的建议使用object.Equals(value,default(T))。它不会调用struct Equals方法,即使您重写了该方法或实现了IEquatable。因此,即使您有一些结构在其相等比较中不需要默认字段值,它也不会崩溃。@Evk I测试了它,并且
object.Equals
似乎正在调用重写的
Equals
。还说“这意味着如果objA重写Object.Equals(Object)方法,则调用该重写。”@PetSerAI,这很有效!如果你提交的话,我会接受你的回答。但是它是否是框结构呢?
object.Equals(value,default(T))
?使用
IEquatable
可以执行
myStruct.Equals(default(T))
其中T是结构的类型
默认值(T)
不一定是结构的安全值,因此调用其
Equals
实现将崩溃。我真的只想在这里进行位比较,在其他地方进行逻辑比较。您可以按照上面的建议使用object.Equals(value,default(T))。它不会调用struct Equals方法,即使您重写了该方法或实现了IEquatable。因此,即使您有一些结构在其相等比较中不需要默认字段值,它也不会崩溃。@Evk I测试了它,并且
object.Equals
似乎正在调用重写的
Equals
。还说“这意味着如果objA重写Object.Equals(Object)方法,则调用该重写。”@PetSerAI,这很有效!如果你提交的话,我会接受你的回答。但它是否是一个盒子结构?