C# 为什么在某些一般情况下必须进行铸造?

C# 为什么在某些一般情况下必须进行铸造?,c#,generics,C#,Generics,在泛型方法中,您需要将返回变量强制转换为一个对象,然后再转换回泛型类型,我理解这通常是为什么,但我不理解的是,当您已经确定返回语句范围内的变量是什么类型时,为什么要这样做。例如,通常我会这样做: public MyType1 obj1; public MyType2 obj2; public T GetObject<T>() { if (obj1 is T) return (T)(object)obj1; else if (obj2 is T) return (T)

在泛型方法中,您需要将返回变量强制转换为一个对象,然后再转换回泛型类型,我理解这通常是为什么,但我不理解的是,当您已经确定返回语句范围内的变量是什么类型时,为什么要这样做。例如,通常我会这样做:

public MyType1 obj1;
public MyType2 obj2;

public T GetObject<T>()
{
    if (obj1 is T) return (T)(object)obj1;
    else if (obj2 is T) return (T)(object)obj2;
    else return default(T);
}
公共MyType1 obj1;
公共MyType2 obj2;
公共T GetObject()
{
如果(obj1为T),则返回(T)(对象)obj1;
否则如果(obj2为T)返回(T)(对象)obj2;
否则返回默认值(T);
}
但我的问题是,为什么我不能像这样移除铸件:

public MyType1 obj1;
public MyType2 obj2;

public T GetObject<T>()
{
    if (obj1 is T) return obj1;
    else if (obj2 is T) return obj2;
    else return default(T);
}
公共MyType1 obj1;
公共MyType2 obj2;
公共T GetObject()
{
如果(obj1为T),则返回obj1;
否则如果(obj2为T)返回obj2;
否则返回默认值(T);
}
当对象是唯一可能的返回类型时,为什么编译器不理解该对象是正确的返回类型?

Avoid
foo是t;(T) foo
表达式。使用
作为
操作符,它可以解决您的问题:

public MyType1 obj1;

public T GetObject<T>() where T : class {
    T ret = obj1 as T;
    if( ret != null ) return ret;
    ret = obj2 as T;
    if( ret != null ) return ret;
    return default(T);
}
公共MyType1 obj1;
public T GetObject(),其中T:class{
T ret=obj1作为T;
如果(ret!=null)返回ret;
ret=作为T的obj2;
如果(ret!=null)返回ret;
返回默认值(T);
}
避免
foo是T;(T) foo
表达式。使用
作为
操作符,它可以解决您的问题:

public MyType1 obj1;

public T GetObject<T>() where T : class {
    T ret = obj1 as T;
    if( ret != null ) return ret;
    ret = obj2 as T;
    if( ret != null ) return ret;
    return default(T);
}
公共MyType1 obj1;
public T GetObject(),其中T:class{
T ret=obj1作为T;
如果(ret!=null)返回ret;
ret=作为T的obj2;
如果(ret!=null)返回ret;
返回默认值(T);
}

您对泛型做出了错误的假设。为了更好地理解这一点,想象一下。假设您有一个基类base和两个子类sub和VerySub

如果您的成员存储为Base,那么编译器只能保证它们是Base。泛型使用的是静态类型系统。在运行时,我们有一个名为GetIfVerySub的简单函数。假设它对Base类型的对象进行了测试,发现它也是一个VerySub。这在编译时是不可知的,所以要返回向下转换的对象,需要执行强制转换或as

这正是泛型函数所要做的,但由于它是泛型函数,它也可以在任何类型上工作,包括与Base不兼容的类型


不过,更好的问题是,你为什么要这样做?

你对泛型做出了错误的假设。为了更好地理解这一点,想象一下。假设您有一个基类base和两个子类sub和VerySub

如果您的成员存储为Base,那么编译器只能保证它们是Base。泛型使用的是静态类型系统。在运行时,我们有一个名为GetIfVerySub的简单函数。假设它对Base类型的对象进行了测试,发现它也是一个VerySub。这在编译时是不可知的,所以要返回向下转换的对象,需要执行强制转换或as

这正是泛型函数所要做的,但由于它是泛型函数,它也可以在任何类型上工作,包括与Base不兼容的类型

不过,更好的问题是,你为什么要这么做?

根据:

如果已知表达式总是true或总是false,则is关键字会导致编译时警告,但通常会在运行时评估类型兼容性

也就是说,
obj1
是否属于
T
类型不是由编译器确定的,而是在代码运行时进行计算。

根据:

如果已知表达式总是true或总是false,则is关键字会导致编译时警告,但通常会在运行时评估类型兼容性


也就是说,
obj1
是否属于
T
类型不是由编译器确定的,而是在代码运行时进行计算。

注意:这不是装箱或取消装箱,除非T实际上是一种值类型(这些术语确实具有特定含义)。对于任何类来说,这都只是一个演员阵容。所有的代码都有味道。都是。此外,这不是装箱,也不是强制转换。问题“为什么编译器不理解对象的返回类型是正确的,而这是唯一可能的类型?”与泛型无关。它不会为
字符串方法(对象arg)
编译
if(arg为字符串)返回arg任意一个。我相信答案很简单,因为规范不允许这种推断。我认为改写一下,这可能是一个有趣的问题。但我仍然认为它有优点。请注意,如果对象是值类型,
is
检查需要一个box指令,因为用于测试的isinst指令采用对象引用,所以第二个示例实际上并没有避免所有装箱。如果类型是引用类型,则不会以任何方式进行装箱,尽管会使用unbox_any指令进行强制转换。注意:这不是装箱或取消装箱,除非T实际上是值类型(这些术语确实具有特定含义)。对于任何类来说,这都只是一个演员阵容。所有的代码都有味道。都是。此外,这不是装箱,也不是强制转换。问题“为什么编译器不理解对象的返回类型是正确的,而这是唯一可能的类型?”与泛型无关。它不会为
字符串方法(对象arg)
编译
if(arg为字符串)返回arg任意一个。我相信答案很简单,因为规范不允许这种推断。我认为改写一下,这可能是一个有趣的问题。但我仍然认为它有优点。请注意,如果对象是值类型,则
is
检查需要一个box指令,因为用于测试的isinst指令采用对象引用,因此您的第二个示例实际上并没有回避所有问题