基类对象作为派生类的参数
本文关键字:参数 派生 对象 基类 | 更新日期: 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
,而将注意力集中在其他方面
想象一下Giraffe
和Elephant
都是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:这是个坏主意。甚至不要尝试。
不能使基构造函数从任何派生方法(包括派生构造函数)的主体内部运行。即使可以,基实例也不会保留任何关于使用哪个构造函数实例化它的信息,因此无法知道应该调用哪个基构造函数。
上面提到的一般情况是,基构造函数可以潜在地修改与基类不直接相关的应用程序状态(例如,通过在某个地方更改静态字段的值)。可以使用反射将属性值从基本实例复制到正在创建的派生实例,但这实际上是不可行的,因为
- 它要求您首先创建一个基础实例——如果基础是
abstract
,或者创建一个有副作用,该怎么办 - 您需要保证基构造函数不会修改应用程序状态。但这里的目标是独立于基本构造函数的操作,所以您回到了原点
不,这是不可能的,也不应该是,因为这没有意义。
如果可能的话,并且您删除/更改了基类构造函数,那么您仍然需要更改创建基类对象的代码,该基类对象将用作派生类构造函数的参数。
此外,并非所有基类都是具体的。你将无法创建一个抽象基类,对吧?
此功能不可用。我想你想要的有点像这样:
假设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。