我必须仔细思考如何很好地解释这个问题。解释似乎和理解一样困难。
想象你有一个基类 Fruit。你有两个子类 Apple 和 Banana。
Fruit
/ \
Banana Apple
您需要创建两个对象:
Apple a = new Apple()
Banana b = new Banana()
对于这两个对象,您可以将它们强制转换为Fruit对象。
Fruit f = (Fruit)a;
Fruit g = (Fruit)b;
您可以将派生类视为其基类。
但是,您不能将基类视为派生类。
a = (Apple)f
让我们将其应用到列表示例中。
假设您创建了两个列表:
List<Fruit> fruitList = new List<Fruit>()
List<Banana> bananaList = new List<Banana>()
您可以这样做...
fruitList.Add(new Apple());
并且
fruitList.Add(new Banana());
因为将它们添加到列表中时,实际上是对它们进行类型转换。你可以这样想...
fruitList.Add((Fruit)new Apple());
fruitList.Add((Fruit)new Banana());
然而,将相同的逻辑应用于反向情况会引起一些问题。
bananaList.Add(new Fruit());
与……相同
bannanaList.Add((Banana)new Fruit());
由于您不能像处理派生类一样处理基类,因此会产生错误。
以防您的问题是为什么会导致错误,我也会解释一下。
这是水果类(Fruit class)
public class Fruit
{
public Fruit()
{
a = 0;
}
public int A { get { return a; } set { a = value } }
private int a;
}
这里是香蕉类
public class Banana: Fruit
{
public Banana(): Fruit()
{
b = 0;
}
public int B { get { return b; } set { b = value; } }
private int b;
}
假设你创建了两个对象
Fruit f = new Fruit();
Banana ba = new Banana();
请记住,香蕉有两个变量 "a" 和 "b",而水果只有一个变量 "a"。所以当你进行以下操作时...
f = (Fruit)b
f.A = 5
你需要创建一个完整的Fruit对象。
但如果你这样做...
ba = (Banana)f;
ba.A = 5;
ba.B = 3; //Error!!!: Was "b" ever initialized? Does it exist?
问题在于你没有创建完整的香蕉类。并非所有的数据成员都被声明/初始化。现在我从淋浴中回来并拿了一点零食,这里就变得有点复杂了。事后看来,我应该在涉及复杂内容时放弃比喻。让我们创建两个新类:
public class Base
public class Derived : Base
他们可以做任何你想要的事情。
现在让我们定义两个函数。
public Base DoSomething(int variable)
{
return (Base)DoSomethingElse(variable);
}
public Derived DoSomethingElse(int variable)
{
}
这有点像“out”如何工作,你应该总是能够像使用基类一样使用派生类,让我们将其应用到接口中。
interface MyInterface<T>
{
T MyFunction(int variable);
}
out/in的关键区别在于,当Generic作为返回类型或方法参数时,即为前者情况。
让我们定义一个实现该接口的类:
public class Thing<T>: MyInterface<T> { }
然后我们创建两个对象:
MyInterface<Base> base = new Thing<Base>
MyInterface<Derived> derived = new Thing<Derived>
如果你要做这件事:
base = derived;
如果您遇到“无法隐式转换”的错误,您有两个选择:1)显式转换它们或2)告诉编译器隐式转换它们。
base = (MyInterface<Base>)derived
或者
interface MyInterface<out T>
{
T MyFunction(int variable);
}
第二种情况是当您的界面类似于以下内容时:
interface MyInterface<T>
{
int MyFunction(T variable);
}
将其再次与两个功能相关联。
public int DoSomething(Base variable)
{
}
public int DoSomethingElse(Derived variable)
{
return DoSomething((Base)variable);
}
希望你能看到情况已经反转,但本质上是相同类型的转换。
再次使用相同的类。
public class Base
public class Derived : Base
public class Thing<T>: MyInterface<T> { }
相同的对象
MyInterface<Base> base = new Thing<Base>
MyInterface<Derived> derived = new Thing<Derived>
如果您尝试将它们设置为相等
base = derived;
如果编译器再次报错,您有与之前相同的选项。
base = (MyInterface<Base>)derived
或者
interface MyInterface<in T>
{
int MyFunction(T variable);
}
通常情况下,当泛型只用作接口方法的返回类型时,请使用“out”;当它将用作方法参数时,请使用“in”。在使用委托时也适用相同的规则。
虽然有一些奇怪的例外,但我不会在这里担心它们。
提前对任何粗心的错误表示抱歉 =)