C# 为什么将基类设置为派生类型只在其设置的范围内起作用?
我发现很难为这个场景找到一个足够描述性的标题,所以我将让代码来完成大部分讨论 考虑协方差,其中可以用派生类型替换基类C# 为什么将基类设置为派生类型只在其设置的范围内起作用?,c#,polymorphism,covariance,C#,Polymorphism,Covariance,我发现很难为这个场景找到一个足够描述性的标题,所以我将让代码来完成大部分讨论 考虑协方差,其中可以用派生类型替换基类 class Base { } class Derived : Base { } 将typeof(Base)传递到此方法并将该变量设置为派生类型是可能的 private void TryChangeType(Base instance) { var d = new Derived(); instance = d; Console.WriteLine(instan
class Base
{
}
class Derived : Base
{
}
将typeof(Base)
传递到此方法并将该变量设置为派生类型是可能的
private void TryChangeType(Base instance)
{
var d = new Derived();
instance = d;
Console.WriteLine(instance.GetType().ToString());
}
但是,当从上述函数的调用者处检查类型时,实例的类型仍然是Base
private void CallChangeType()
{
var b = new Base();
TryChangeType(b);
Console.WriteLine(b.GetType().ToString());
}
由于对象本质上是引用的,所以我假设调用者变量现在是派生的类型。使调用者成为typeDerived
的唯一方法是通过ref
传递引用对象,如下所示
private void CallChangeTypeByReference()
{
var b = new Base();
TryChangeTypeByReference(ref b);
Console.WriteLine(b.GetType().ToString());
}
private void TryChangeTypeByReference(ref Base instance)
{
var d = new Derived();
instance = d;
}
此外,我觉得将一个对象传递给一个方法、编辑道具并将该对象传递给堆栈将保持堆栈中所做的更改是常识。这是有意义的,因为对象是引用对象
是什么导致一个对象永久性地改变堆栈中的类型,只有当它通过引用传入时?我们知道类是引用类型,所以一般来说,当我们传递类型时,我们传递的是引用,但是只传递
b
和ref b
之间有区别,这可以理解为:
private Base TryChangeType(Base instance)
{
var d = new Derived();
instance = d;
Console.WriteLine(instance.GetType().ToString());
return instance;
}
private void CallChangeType()
{
var b = new Base();
b = TryChangeType(b);
Console.WriteLine(b.GetType().ToString());
}
以下是两个案例的参考图片:
如果不通过引用传递,则会在函数内传递基类对象的副本,并且会在TryChangeType函数内更改此副本。打印基类实例的类型时,它仍然是“base”类型的类型,因为实例的副本已更改为“派生”类
当您通过referece传递时,实例的地址(即instace本身)将传递给函数。因此,对函数内实例所做的任何更改都是永久性的。这不是继承和多态性的问题,您看到的是按值传递和按引用传递之间的差异
private void TryChangeType(Base instance)
前面方法的实例参数将是调用方基本引用的副本。您可以更改被引用的对象,这些更改将对调用方可见,因为调用方和被调用方都引用同一个对象。但是,对引用本身的任何更改(例如将其指向新对象)都不会影响调用方的引用。这就是为什么当你通过引用时,它会像预期的那样工作。你有很多困惑和错误的信念。让我们来解决这个问题
private void TryChangeType(Base instance)
考虑协方差,其中可以用派生类型替换基类
class Base
{
}
class Derived : Base
{
}
这不是协方差。这就是分配兼容性。Apple的赋值与Fruit类型的变量兼容,因为您可以将Apple赋值给此类变量。同样,这不是协方差。协方差是一个事实,即类型上的转换保留了赋值兼容性关系。苹果序列可以用在需要水果序列的地方,因为苹果是一种水果这是协方差。映射“苹果-->苹果序列,水果-->水果序列”是协变映射
继续
将typeof(Base)
传递到此方法并将该变量设置为派生类型是可能的
您将类型与实例混淆。不将typeof(Base)
传递给此方法;将对Base
的引用传递到此实例typeof(Base)
属于System.type类型
正如您正确指出的,形式参数是变量。形式参数是一个新变量,它被初始化为实际参数aka参数
但是,当从上述函数的调用方检查类型时,实例仍然是Base类型
对。参数的类型为Base
。将其复制到变量,然后重新分配变量。这无异于说:
Base x = new Base();
Base y = x;
y = new Derived();
现在x
仍然是Base
,y
是派生的
。您为同一变量指定了两次;第二项任务获胜。这和你说的a=1没有什么不同;b=a;b=2
--你不会仅仅因为你过去说过b=a
,就期望a
之后是2
我假设,由于对象本质上是引用,调用方变量现在将是派生类型
这种假设是错误的。同样,您对同一变量进行了两次赋值,并且您有两个变量,一个在调用者中,一个在被调用者中变量包含值;对对象的引用是值
唯一的办法