基类对象作为派生类的参数

本文关键字:参数 派生 对象 基类 | 更新日期: 2024-09-22 23:41:02

(简化)场景:

public class BaseClass
{
    public int BaseClassInt {get; set;}
    public BaseClass(int pBaseClassInt)
    { this.BaseClassInt = pBaseClassInt; }
}
public class DerivedClass : BaseClass
{
    public int DerivedClassInt {get; set;}
    public DerivedClass (int pBaseClassInt, int pDerivedClassInt) : base(pBaseClassInt)
    { this.DerivedClassInt = pDerivedClassInt; }
}

如果我想实例化DerivedClass对象,我必须传递创建BaseClass对象和DerivedCClass对象所需的所有参数。此外,对于每个BaseClass构造函数,我必须(至少在我的具体情况下应该)在派生类中提供一个具有相同参数的构造函数,以及派生类属性的参数。然后,如果我更改或删除基类中的构造函数,我必须更改或删除派生类中相应的构造函数。

我想知道是否可以为接受基类对象作为参数的派生类使用构造函数:

public DerivedClass(BaseClass pBaseClassObejct, int pDerivedClassInt)
{
    // to make clear what I intend to do - looks silly of course
    this = (DerivedClass)pBaseClassObject;
    this.DerivedClassInt = pDerivedClassInt;
}

这可以称为:

DerivedClass DerivedClassObject = new DerivedClass((new BaseClass(1),2);

如果基类中的构造函数会发生变化,我就不必为派生类介意了。有什么办法做到这一点吗?

基类对象作为派生类的参数

想一想这行:

this = (DerivedClass) pBaseClassObject;

让我们忽略这样一个事实,即不能直接设置this,而将注意力集中在其他方面

想象一下GiraffeElephant都是AfricanAnimal:的实现

// By extension, ellie is also an AfricanAnimal    
Elephant ellie = new Elephant(); 
// assume ellie is passed in as a param here (she can 
// be, because she is an AfricanAnimal after all!):
public Giraffe(AfricanAnimal ellie) 
{
    this = (Giraffe) ellie; // Can't do this!
}

你不能(也不想)强迫ellie成为长颈鹿,因为长颈鹿可能有ellie缺乏的特性等,而ellie可能有长颈鹿没有的特性。然而,在那里使用AfricanAnimal作为参数类型,就可以做到这一点。

注意:可以编写代码并传入长颈鹿,一切都会很好,但话说回来,这没有什么意义;那么您也可以使用Giraffe类型作为参数。

如果用实例变量替换this,则可以使用以下内容编译。。。

public Giraffe(AfricanAnimal ellie) 
{
    this.varOfTypeGiraffe = (Giraffe) ellie; 
}

但一旦你用Elephant作为婴儿车运行它,你就会得到一个类似于的异常

InvalidCastException:无法将"Elephant"类型的对象强制转换为"Giraffe"类型。

TL;DR:这是个坏主意。甚至不要尝试。

不能使基构造函数从任何派生方法(包括派生构造函数)的主体内部运行。即使可以,基实例也不会保留任何关于使用哪个构造函数实例化它的信息,因此无法知道应该调用哪个基构造函数。

上面提到的一般情况是,基构造函数可以潜在地修改与基类不直接相关的应用程序状态(例如,通过在某个地方更改静态字段的值)。可以使用反射将属性值从基本实例复制到正在创建的派生实例,但这实际上是不可行的,因为

  1. 它要求您首先创建一个基础实例——如果基础是abstract,或者创建一个有副作用,该怎么办
  2. 您需要保证基构造函数不会修改应用程序状态。但这里的目标是独立于基本构造函数的操作,所以您回到了原点

不,这是不可能的,也不应该是,因为这没有意义。

如果可能的话,并且您删除/更改了基类构造函数,那么您仍然需要更改创建基类对象的代码,该基类对象将用作派生类构造函数的参数。

此外,并非所有基类都是具体的。你将无法创建一个抽象基类,对吧?

此功能不可用。我想你想要的有点像这样:

假设C#有一个关键字allbaseargs,并允许这样的代码:

public class DerivedClass : BaseClass
{
  public int DerivedClassInt { get; set; }
  public DerivedClass (allbaseargs, int pDerivedClassInt)
    : base(allbaseargs)
  {
    DerivedClassInt = pDerivedClassInt;
  }
}

那么,只有当BaseClass只有一个(可访问)实例构造函数时,这才能工作。

然后编译器应该检查唯一的基构造函数,并用该构造函数的参数替换神奇的单词allbaseargs

然而,C#没有这个特性,您必须手动编写所有代码,其中包括在构造函数签名更改时更改所有派生类的所有: base(...)调用。

允许有签名:

public DerivedClass(BaseClass pBaseClassObejct, int DerivedClassInt)

就像你建议的那样,但你不能轻易地连锁: base(...)。您必须为BaseClass配备一个构造器,该构造器在中获取另一个实例,并将该实例的所有"状态"(所有实例属性和字段等)复制到"this"。我不建议采用这种解决方案。

这可能会有所帮助!

解决方案A:创建继承而不是基础

public static class Test
{
    public static T Foo<T>(string text, int num) where T : BaseClass
    {
        T @base = (T)Activator.CreateInstance(typeof(T), new object[] { text, num });
        //...
        return @base;
    }
    public static void Main()
    {
        InheritClass inherit = Foo<InheritClass>("Hi there", 10);
    }
}

解决方案B:复制基础以继承

public static class Test
{
    public static TInherit As<TBase, TInherit>(this TBase @this) where TInherit : TBase
    {
        var type = typeof(TInherit);
        var instance = Activator.CreateInstance(type);
        foreach (var property in type.GetProperties())
            if (property.CanWrite)
                property.SetValue(instance, property.GetValue(@this, null), null);
        return (TInherit)instance;
    }
    public static BaseClass Foo(string text, int num)
    {
        BaseClass @base = new BaseClass(text, num);
        //...
        return @base;
    }
    public static void Main()
    {
        InheritClass inherit = Foo("Hi there", 10).As<BaseClass, InheritClass>();
    }
}

注意:这里可以找到简单的"As()",但我更喜欢我的(Inherit:TBase),因为它更安全,并且支持将base转换为Inherit类的Inherit。