一种将基类型转换为派生类型的方法

时间:2008-09-23 22:32:54

标签: c# oop inheritance design-patterns

我不确定这是否是一件奇怪的事情,或者是否有一些代码味道...但我想知道是否有某种方法(某种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());
}

我知道这似乎很傻,但有没有办法完成这类事情呢?

17 个答案:

答案 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)
    ;

-edit- I asked this question long ago

答案 13 :(得分:0)

这是不可能的,因为你将如何得到派生类所具有的“额外”。当你实例化它时,编译器如何知道你的意思是derivedClass1而不是derivedClass2?

我认为您真正需要的是工厂模式或类似工具,因此您可以在不知道实例化的显式类型的情况下实例化对象。在您的示例中,使用“Insert”方法将是工厂返回实例的实例的接口。

答案 14 :(得分:0)

这被称为向下转发,而Seldaek建议使用“安全”版本是合理的。

这是一个相当不错的description with code samples

答案 15 :(得分:0)

那不行。查看由编译错误链接的帮助页面。

最好的解决方案是在这里使用工厂方法。

答案 16 :(得分:-1)

C ++使用构造函数处理它。 C++ Typecasting。这似乎是对我的疏忽。你们中的许多人提出了这个过程对额外属性的影响。我会回答,当程序员没有设置属性时,编译器在创建派生类时会做什么?我已经处理过与C ++类似的情况。我创建一个构造函数,它接受基类,然后在构造函数中手动设置属性。这绝对比在派生类中设置变量和破坏继承更可取。我也会通过工厂方法选择它,因为我认为生成的代码看起来会更干净。