我不确定这是否是一件奇怪的事情,或者是否有一些代码味道...但我想知道是否有某种方法(某种oop模式会很好)将基类型“转换”为其派生类型的形式。我知道这没有多大意义,因为派生类型将具有父级不提供的附加功能,这在其本身并非基本健全。但有没有办法做到这一点?这是一个代码示例,以便我可以更好地解释我在问什么。
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());
}
我知道这似乎很傻,但有没有办法完成这类事情呢?
答案 0 :(得分:30)
在“托管”语言中并不合理。这是向下转换,并没有理智的方式来处理它,正是因为你描述的原因(子类提供的不仅仅是基类 - 这个“更多”来自哪里?)。如果您确实需要特定层次结构的类似行为,则可以将构造函数用于将基类型作为原型的派生类型。
可以用反射来构建一些处理简单情况的东西(更具体的类型没有添加状态)。一般来说,只需重新设计即可避免此问题。
编辑:Woops,无法在基类/派生类型之间编写转换运算符。微软试图“保护你”对付自己的奇怪之处。好吧,至少他们不像太阳那么差。
答案 1 :(得分:17)
尝试使用合成而不是继承!
在我看来,你最好将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(第3页):
通过合成组合重用代码 为Apple提供了另一种方式 重用Fruit的实现 剥()。而不是延伸水果, Apple可以提供Fruit的参考 实例并定义自己的peel() 简单地调用peel()的方法 水果。
答案 2 :(得分:15)
我个人认为在这种情况下使用继承的麻烦并不值得。而只是在构造函数中传递基类实例并通过成员变量访问它。
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
{
}
}
}
答案 3 :(得分:9)
向下转换是有意义的,如果你有一个派生类的对象,但它被基类类型的引用引用,并且由于某种原因你希望它返回由派生类类型引用引用。换句话说,您可以向下转换以反转先前向上转换的效果。但是,您不能拥有由派生类类型的引用引用的基类对象。
答案 4 :(得分:7)
我不是说我推荐这个。但是您可以将基类转换为JSON字符串,然后将其转换为派生类。
SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject));
答案 5 :(得分:5)
C#语言不允许这样的操作符,但您仍然可以编写它们并且它们可以工作:
[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Implicit(Base a) { ... }
[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Explicit(Base a) { ... }
答案 6 :(得分:5)
不,这是不可能的。在像C#这样的托管语言中,它只是不起作用。即使编译器允许,运行时也不会允许它。
你自己说这看起来很傻:
SomeBaseClass class = new SomeBaseClass();
SomeDerivedClass derClass = (SomeDerivedClass)class;
那么问问自己,class
实际上是SomeDerivedClass
的一个实例吗?不,所以转换毫无意义。如果您需要将SomeBaseClass
转换为SomeDerivedClass
,那么您应该提供某种转换,无论是构造函数还是转换方法。
听起来好像你的类层次结构需要一些工作。通常,不应该可以将基类实例转换为派生类实例。通常应该存在不适用于基类的数据和/或功能。如果派生类功能适用于基类的所有实例,那么它应该被卷入基类或者拉入不属于基类层次结构的新类。
答案 7 :(得分:1)
是的 - 这是一种代码味道,并且几乎指出了你的继承链被破坏的事实。
我的猜测(来自有限的样本)是你宁愿让DerivedClass对SomeBaseClass的一个实例进行操作 - 所以“DerivedClass 有一个 SomeBaseClass”,而不是比“DerivedClass 是 SomeBaseClass”。这被称为“赞成组合而不是继承”。
答案 8 :(得分:1)
正如其他人所说,你建议的演员阵容真的不可能。 是否可以引入Decorator pattern(Head First提取物)?
答案 9 :(得分:1)
您是否考虑过一个接口,即当前您的基类和派生类都将实现?我不知道为什么你这样做的具体细节,但它可能有效。
答案 10 :(得分:0)
正如许多答案所指出的那样,你不能贬低这是完全有道理的。
但是,在您的情况下,SomeDerivedClass
没有遗失的属性&#39;。所以你可以创建一个像这样的扩展方法:
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>();
只有当基类中的所有数据都是可读写属性时才能正常工作。
答案 11 :(得分:0)
我最近一直需要使用派生类型扩展一个简单的DTO,以便在其上添加更多属性。然后我想重用一些转换逻辑,从内部数据库类型到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中的转换逻辑。当然,我必须以其他方式填写复杂类型的属性,但是简单DTO的许多属性,这真的简化了IMO。
答案 12 :(得分:0)
我不知道为什么没有人这么说,我可能会错过一些东西,但你可以使用as关键字,如果你需要使用if语句,请使用if。
SomeDerivedClass derClass = class as SomeDerivedClass; //derClass is null if it isnt SomeDerivedClass
if(class is SomeDerivedClass)
;
答案 13 :(得分:0)
这是不可能的,因为你将如何得到派生类所具有的“额外”。当你实例化它时,编译器如何知道你的意思是derivedClass1而不是derivedClass2?
我认为您真正需要的是工厂模式或类似工具,因此您可以在不知道实例化的显式类型的情况下实例化对象。在您的示例中,使用“Insert”方法将是工厂返回实例的实例的接口。
答案 14 :(得分:0)
这被称为向下转发,而Seldaek建议使用“安全”版本是合理的。
这是一个相当不错的description with code samples。
答案 15 :(得分:0)
那不行。查看由编译错误链接的帮助页面。
最好的解决方案是在这里使用工厂方法。
答案 16 :(得分:-1)
C ++使用构造函数处理它。 C++ Typecasting。这似乎是对我的疏忽。你们中的许多人提出了这个过程对额外属性的影响。我会回答,当程序员没有设置属性时,编译器在创建派生类时会做什么?我已经处理过与C ++类似的情况。我创建一个构造函数,它接受基类,然后在构造函数中手动设置属性。这绝对比在派生类中设置变量和破坏继承更可取。我也会通过工厂方法选择它,因为我认为生成的代码看起来会更干净。