Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/266.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/arduino/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么将基类设置为派生类型只在其设置的范围内起作用?_C#_Polymorphism_Covariance - Fatal编程技术网

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());
}  
由于对象本质上是引用的,所以我假设调用者变量现在是派生的类型。使调用者成为type
Derived
的唯一方法是通过
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
之间有区别,这可以理解为:

  • 在第一种情况1中,它通过值传递引用,这意味着在内存位置内部创建一个单独的指针,现在当基类对象分配给派生类对象时,它开始指向内存中的另一个对象,当该方法返回时,只保留原始指针,它提供与基类相同的实例,当创建的新指针为垃圾收集关闭时
  • <>但是,当对象被传递为<强> REF,它在内存中传递<强>引用到引用< /强>,这就像指针的指针,像C或C++中的双指针,当改变实际上改变了原来的内存分配,因此你看到了差异 对于第一个显示相同结果的对象,必须从方法返回值,并且旧对象应开始指向新的派生对象

    以下是为在案例1中获得预期结果而对程序进行的修改:

    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

    我假设,由于对象本质上是引用,调用方变量现在将是派生类型

    这种假设是错误的。同样,您对同一变量进行了两次赋值,并且您有两个变量
    ,一个在调用者中,一个在被调用者中变量包含值;对对象的引用是值

    唯一的办法