将基types转换为派生types的一种方法
我不确定这是否是一件奇怪的事情,或者如果它是一些代码味道…但我想知道是否有一种方法(某种types的oop模式会很好)来“投”基types转换为其派生types的forms。 我知道这是没有意义的,因为派生types将具有父类不提供的附加function,这在本身并不是根本上合理的。 但有没有办法做到这一点? 这里是一个代码示例,以便我可以更好地解释我所要求的。
public class SomeBaseClass { public string GetBaseClassName {get;set;} public bool BooleanEvaluator {get;set;} } public class SomeDerivedClass : SomeBaseClass { public void Insert(SqlConnection connection) { //...random connection stuff cmd.Parameters["IsItTrue"].Value = this.BooleanEvalutar; //... } } public static void Main(object[] args) { SomeBaseClass baseClass = new SomeBaseClass(); SomeDerivedClass derClass = (SomeDerivedClass)baseClass; derClass.Insert(new sqlConnection()); }
我知道这似乎是愚蠢的,但有什么办法来完成这样的事情吗?
不完善,在“托pipe”的语言。 这是向下的 ,没有理智的方式来处理它,因为你描述的原因(子类提供的不仅仅是基类 – 这个“更多”来自哪里?)。 如果您确实需要特定层次结构的类似行为,则可以使用派生types的构造函数,将基types作为原型。
可以用reflection来构build一些处理简单情况的东西(更具体的没有附加状态的types)。 一般来说,只需重新devise就可以避免这个问题。
编辑:Woops,不能在基类/派生types之间写转换运算符。 微软公司试图“保护你”不利于自己。 好吧,至less他们不像Sun那么糟糕。
尝试组合而不是inheritance!
在我看来,你最好将SomeBaseClass的实例传递给SomeDerivedClass(它将不再派生基类,并应该重新命名为这样)
public class BooleanHolder{ public bool BooleanEvaluator {get;set;} } public class DatabaseInserter{ BooleanHolder holder; public DatabaseInserter(BooleanHolder holder){ this.holder = holder; } public void Insert(SqlConnection connection) { ...random connection stuff cmd.Parameters["IsItTrue"].Value = holder.BooleanEvalutar; ... } } public static void Main(object[] args) { BooleanHolder h = new BooleanHolder(); DatabaseInserter derClass = new DatabaseInserter(h); derClass.Insert(new sqlConnection); }
查看http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html(page 3):
通过合成代码重用代码复用为苹果重新使用Fruit的peel()实现提供了另一种方式。 苹果可以持有对Fruit实例的引用,并定义自己的peel()方法,该方法只需在Fruit上调用peel()方法。
就我个人而言,我认为在这种情况下使用inheritance的麻烦是不值得的。 相反,只需在构造函数中传入基类实例,并通过成员variables来访问它。
private class ExtendedClass //: BaseClass - like to inherit but can't { public readonly BaseClass bc = null; public ExtendedClass(BaseClass b) { this.bc = b; } public int ExtendedProperty { get { } } }
向下转换是有意义的,如果你有一个派生类的对象,但是它被引用的基类types引用,并且由于某种原因,你希望它被派生类types引用引用。 换句话说,您可以向下倒转以反转之前的上传效果。 但是你不能有一个由派生类types的引用引用的基类对象。
我不是说我推荐这个。 但是你可以把基类转换成JSONstring,然后将其转换为派生类。
SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject));
不,这是不可能的。 在像C#这样的托pipe语言中,它不会工作。 即使编译器允许,运行时也不会允许它。
你说你自己这似乎愚蠢:
SomeBaseClass class = new SomeBaseClass(); SomeDerivedClass derClass = (SomeDerivedClass)class;
那么问问自己,实际上class
是SomeDerivedClass
一个实例吗? 不,所以转换没有意义。 如果您需要将SomeBaseClass
转换为SomeDerivedClass
,那么您应该提供某种转换,无论是构造函数还是转换方法。
这听起来好像你的类层次需要一些工作,但。 通常, 不应该可以将基类实例转换为派生类实例。 通常应该有不适用于基类的数据和/或function。 如果派生类function适用于基类的所有实例,那么它应该被汇总到基类中,或者被拉入不属于基类层次结构的新类中。
C#语言不允许这样的操作符,但是你仍然可以编写它们并且它们工作:
[System.Runtime.CompilerServices.SpecialName] public static Derived op_Implicit(Base a) { ... } [System.Runtime.CompilerServices.SpecialName] public static Derived op_Explicit(Base a) { ... }
是的 – 这是一种代码异味,而且很大程度上减less了inheritance链被破坏的事实。
我的猜测 (从有限的样本)是,你宁愿让DerivedClass操作SomeBaseClass的一个实例 – 所以“DerivedClass 有一个 SomeBaseClass”,而不是“DerivedClass 是一个 SomeBaseClass”。 这就是所谓的“偏爱inheritance”。
正如其他人所指出的那样,你build议的这个演员并不是真的可能。 这可能是介绍装饰者模式 (Head First extract)的情况吗?
你有没有想过一个接口,目前你的基类和你的派生类都将实现? 我不知道你为什么这样实施的具体细节,但它可能工作。
这被称为downcasting和Seldaek的build议使用“安全”的版本是健全的。
这里有一个相当不错的代码示例描述 。
这是不可能的,因为你将如何获得派生类的“额外”。 编译器在实例化时如何知道你的意思是derivedClass1而不是derivedClass2?
我认为你真正想要的是工厂模式或类似的,所以你可以实例化对象,而不必真正知道正在实例化的显式types。 在你的例子中,使用“Insert”方法将是一个实例工厂返回实现的接口。
我不知道为什么没有人这样说,我可能会错过一些东西,但你可以使用as关键字,如果你需要做一个if语句使用if。
SomeDerivedClass derClass = class as SomeDerivedClass; //derClass is null if it isnt SomeDerivedClass if(class is SomeDerivedClass) ;
– 编辑 – 我早就问过这个问题
我最近需要用派生types来扩展一个简单的DTO,以便在其上添加更多的属性。 然后我想重用一些从内部数据库types到DTO的转换逻辑。
我解决这个问题的方法是在DTO类上强制使用一个空的构造函数,如下所示:
class InternalDbType { public string Name { get; set; } public DateTime Date { get; set; } // Many more properties here... } class SimpleDTO { public string Name { get; set; } // Many more properties here... } class ComplexDTO : SimpleDTO { public string Date { get; set; } } static class InternalDbTypeExtensions { public static TDto ToDto<TDto>(this InternalDbType obj) where TDto : SimpleDTO, new() { var dto = new TDto { Name = obj.Name } } }
然后,我可以在转换为复杂的DTO时重用简单的DTO中的转换逻辑。 当然,我将不得不以其他方式填写复杂types的属性,但是具有简单DTO的许多属性,这实际上简化了IMO的事情。
这是行不通的。 去看编译错误链接的帮助页面。
最好的解决scheme是在这里使用工厂方法。
正如许多答案指出的那样,你不能沮丧,这是完全合理的。
但是,在你的情况下, SomeDerivedClass
没有属性,将'失踪'。 所以你可以创build一个像这样的扩展方法:
public static T ToDerived<T>(this SomeBaseClass baseClass) where T:SomeBaseClass, new() { return new T() { BooleanEvaluator = baseClass.BooleanEvaluator, GetBaseClassName = baseClass.GetBaseClassName }; }
所以你不是铸造,只是转换:
SomeBaseClass b = new SomeBaseClass(); SomeDerivedClass c = b.ToDerived<SomeDerivedClass>();
如果基类中的所有数据都以可读写属性的forms存在,那么这才真正起作用。
C ++使用构造函数处理它。 C ++types转换 。 这对我来说似乎是一个疏忽。 你们中的许多人提出了这个过程对于额外属性会做什么的问题。 我会回答,编译器在程序员没有设置属性时创build派生类时会做什么? 我已经处理了类似于C ++的这种情况。 我创build一个构造函数,接受基类,然后在构造函数中手动设置属性。 这对于在派生类中设置variables并中断inheritance是绝对可取的。 我也会select它在工厂方法,因为我认为结果代码将更清洁。