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

时间:2013-12-13 10:54:01

标签: c# constructor deriving

(简化)场景:

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对象和DerivedClass对象所需的所有参数。此外,对于每个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);

如果基类中的构造函数会改变,我不必为派生类考虑它。有没有办法实现这个目标?

5 个答案:

答案 0 :(得分:3)

暂时考虑一下这句话:

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!
}

你不能(也不想)迫使艾莉成为长颈鹿,因为长颈鹿可能具有艾莉缺乏的属性等,而且艾莉可能具有长颈鹿所没有的属性。然而,在那里使用AfricanAnimal作为参数类型,只允许这样做。

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

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

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

...但是只要您使用Elephant作为参数运行它,您将获得类似于以下内容的异常:

  

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

答案 1 :(得分:2)

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

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

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

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

答案 2 :(得分:2)

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

如果可以删除/更改基类构造函数,则仍需要更改创建基类对象的代码,该对象将用作派生类构造函数的参数。

此外,并非所有基类都具体。您将无法创建抽象基类,对吧?

答案 3 :(得分:1)

此功能不可用。我想你想要的是这样的:

假设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装备另一个实例的construtor并将所有“state”(所有实例属性和字段等)从该其他实例复制到“this”。我不推荐这个解决方案。

答案 4 :(得分:0)

这可能有帮助!

解决方案 A:创建 Inherit 而不是 base!

config.json

方案B:复制基类继承

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);
    }
}

注意:您可以找到简单的 'As()' here,但我更喜欢我的(其中 Inherit : TBase),它更安全并且支持将 base 转换为继承类的继承。