.net 是否可以在没有反射的情况下将装箱值类型与引用类型区分开来

.net 是否可以在没有反射的情况下将装箱值类型与引用类型区分开来,.net,struct,boxing,.net,Struct,Boxing,如果有一个例程接受IEnumerator类型的参数,那么有没有办法区分对类类型实例的引用和对装箱值类型实例的引用?我知道如果一个人有这样的习惯: void DoSomething<T>(T param) where T:IEnumerator 只需执行Object.ReferenceEqualsparam,param,就可以在不使用反射的情况下判断T是值类型还是引用类型。如果T是类类型,它将始终返回true;如果它是值类型,它将始终返回false。因为它实现了一个接口,所以它不能是可

如果有一个例程接受IEnumerator类型的参数,那么有没有办法区分对类类型实例的引用和对装箱值类型实例的引用?我知道如果一个人有这样的习惯:

void DoSomething<T>(T param) where T:IEnumerator 只需执行Object.ReferenceEqualsparam,param,就可以在不使用反射的情况下判断T是值类型还是引用类型。如果T是类类型,它将始终返回true;如果它是值类型,它将始终返回false。因为它实现了一个接口,所以它不能是可为null的值类型。另一方面,如果要调用DoSomethingmyListOfStrings.GetEnumerator,我不知道DoSomething是否能够在不使用反射的情况下,将装箱IEnumerator的行为与任何其他类类型的行为区分开来。装箱值类型的行为与类类型的行为是否存在任何与非反射相关的方式

附录

为了澄清这个问题,假设我有一个实现IFoo的结构S1,还有一个实现相同接口的类C1,并且有完全相同的方法和字段。如果有一个声明类型为IFoo的变量,那么该变量引用的boxedS1实例的“自然”行为是否与同样引用的C1实例的“自然”行为有所不同,除了GetType当然会返回不同的类型对象之外?

您可以使用type.IsValueType属性。以下是一个例子:

    static void Main()
    {
        DoSomething(10);            // True
        DoSomething((object)10);    // True
        DoSomething(new Random());  // False
    }

    public static void DoSomething<T>(T arg)
    {
        bool isValueType = arg.GetType().IsValueType;
        Console.WriteLine(isValueType);
    }
您可以使用Type.IsValueType属性。以下是一个例子:

    static void Main()
    {
        DoSomething(10);            // True
        DoSomething((object)10);    // True
        DoSomething(new Random());  // False
    }

    public static void DoSomething<T>(T arg)
    {
        bool isValueType = arg.GetType().IsValueType;
        Console.WriteLine(isValueType);
    }
如果您有一个对象e,它的静态类型是IEnumerator之类的接口,并且您想知道它是否是装箱值类型,您可以简单地使用:

e is ValueType
是.Net中所有值类型的公共基类。

如果您有一个对象e,其静态类型是IEnumerator之类的接口,并且您想知道它是否是装箱值类型,则可以简单地使用:

e is ValueType

是.Net中所有值类型的公共基类。

我不知道这是否会被视为一个明显的差异,但我认为值得注意的是,当一个结构被装箱两次时,两个装箱的对象将不相等。这是有意义的,因为结构没有引用标识。不过,我认为这不太可能出现

我下面的例子很做作

public class BoxDemo
{
    public interface IFoo { }
    public class CFoo : IFoo { }
    public struct SFoo : IFoo { }

    public static class TestBoxing
    {
        public static void dotest()
        {
            CFoo C = new CFoo();
            SFoo S = new SFoo();

            Debug.WriteLine(IsBoxedStruct(C, C)); // writes "false"
            Debug.WriteLine(IsBoxedStruct(S, S)); // writes "true"
        }

        public static bool IsBoxedStruct(IFoo f1, IFoo f2)
        {
            return !object.ReferenceEquals(f1, f2);
        }
    }
}

我不知道这是否会被认为是一个明显的区别,但我认为值得注意的是,当一个结构被装箱两次时,两个装箱的对象将不相等。这是有意义的,因为结构没有引用标识。不过,我认为这不太可能出现

我下面的例子很做作

public class BoxDemo
{
    public interface IFoo { }
    public class CFoo : IFoo { }
    public struct SFoo : IFoo { }

    public static class TestBoxing
    {
        public static void dotest()
        {
            CFoo C = new CFoo();
            SFoo S = new SFoo();

            Debug.WriteLine(IsBoxedStruct(C, C)); // writes "false"
            Debug.WriteLine(IsBoxedStruct(S, S)); // writes "true"
        }

        public static bool IsBoxedStruct(IFoo f1, IFoo f2)
        {
            return !object.ReferenceEquals(f1, f2);
        }
    }
}

就我个人而言,我会使用一些涉及typeofT.IsValueType的东西。如果我绝对需要的话,可能会将结果缓存在静态泛型类中……为什么要尝试避免反射?而且,您的代码无法编译。你是说doSomethingMyListofString.GetEnumerator吗?@svick:IEnumeratable已更改为IEnumerator。谢谢更新问题以进一步澄清。就个人而言,我只会使用一些涉及typeofT.IsValueType的内容。如果我绝对需要的话,可能会将结果缓存在静态泛型类中……为什么要尝试避免反射?而且,您的代码无法编译。你是说doSomethingMyListofString.GetEnumerator吗?@svick:IEnumeratable已更改为IEnumerator。谢谢更新问题以进一步澄清。Type.IsValueType需要反射,不是吗?@supercat:它将使用Type.BaseType属性在类型层次结构中遍历,并检查父级是否为TypeRuntimeType.ValueType类型。此时,所有基本类型对象都已经加载到AppDe域中,所以这种方法似乎没有引入任何性能影响。也许我应该在我的问题中更清楚,因为我考虑GETType,并且几乎所有与显式引用的类型对象相关联的对象都是反射。问题不在于性能,而在于语义。未绑定结构的行为将明显不同于具有相同方法和字段的类的行为。但是,给定一个装箱结构,以及一个具有相同方法和字段的类,除了通过GetType之外,是否还有其他方法来区分它们?Type.IsValueType需要反射,不是吗?@supercat:它将使用Type.BaseType属性遍历类型层次结构,并检查父类型是否为TypeRuntimeType.ValueType。此时,所有基本类型对象都已经加载到AppDe域中,所以这种方法似乎没有引入任何性能影响。也许我应该在我的问题中更清楚,因为我考虑GETType,并且几乎所有与显式引用的类型对象相关联的对象都是反射。问题不在于性能,而在于语义。未固定结构的行为将明显不同
使用相同方法和字段的类的nt。然而,给定一个装箱结构,以及一个具有相同方法和字段的类,除了通过GetType之外,还有什么方法可以区分它们呢?这里的角点是一个实现IEnumerable的结构。我花了一段时间才明白为什么IEnumerator被引用,始终是ref类型+1对于最漂亮的答案,它隐藏了is操作符非常好地使用的反射形式。这是一种快速的类型,类型信息未映射的几率很低。@HansPassant:我的问题是,一个装箱结构的行为与一个具有相同字段和方法的类的行为有何不同,而不是系统说一个是值类型,另一个是类类型。非装箱值类型的行为与类类型非常不同,但装箱值类型实例的行为在我能识别的各个方面都与类类型完全相同,除了GetType的返回值。还有什么区别?不知道你的意思。拳击运动本身就相当庞大。结构的完整副本嵌入到必须从GC堆分配的内存块中。与之相比,接下来发生的事情微不足道。毕竟,现在它是一个类对象,与常规引用类型对象没有太大区别。@HansPassant:创建一个结构并装箱它往往比创建一个类实例慢一点,尽管我希望对于简单的结构/类,与创建新的类实例和加快下一次垃圾收集相关的摊销GC时间相比,额外的时间会很短。我的问题是,在什么情况下(如果有的话),装箱结构的接收者可能会观察到与具有完全相同的方法和字段的类的行为意外不同的任何行为。我花了一段时间才明白为什么IEnumerator被引用,始终是ref类型+1对于最漂亮的答案,它隐藏了is操作符非常好地使用的反射形式。这是一种快速的类型,类型信息未映射的几率很低。@HansPassant:我的问题是,一个装箱结构的行为与一个具有相同字段和方法的类的行为有何不同,而不是系统说一个是值类型,另一个是类类型。非装箱值类型的行为与类类型非常不同,但装箱值类型实例的行为在我能识别的各个方面都与类类型完全相同,除了GetType的返回值。还有什么区别?不知道你的意思。拳击运动本身就相当庞大。结构的完整副本嵌入到必须从GC堆分配的内存块中。与之相比,接下来发生的事情微不足道。毕竟,现在它是一个类对象,与常规引用类型对象没有太大区别。@HansPassant:创建一个结构并装箱它往往比创建一个类实例慢一点,尽管我希望对于简单的结构/类,与创建新的类实例和加快下一次垃圾收集相关的摊销GC时间相比,额外的时间会很短。我的问题是,在什么情况下(如果有的话),装箱结构的接收者可能会观察到与具有完全相同的方法和字段的类的行为意外不同的任何行为;然后Debug.WriteLineIsBoxedStructI,I;通过两次传递参数来测试的不是被调用函数是否接收装箱结构,而是调用方是否具有非装箱结构。顺便说一句,值类型实例方法很难确定它是否引用特定的装箱实例,我不知道值类型实例如何确定是否已装箱;然后Debug.WriteLineIsBoxedStructI,I;通过两次传递参数来测试的不是被调用函数是否接收装箱结构,而是调用方是否具有非装箱结构。顺便说一句,值类型实例方法很难确定它是否引用特定的装箱实例,我知道没有办法让值类型实例来确定是否已装箱。