简体   繁体   English

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

[英]A way of casting a base type to a derived type

I'm not sure if this is a strange thing to do or not, or if it is some how code smell...but I was wondering if there was a way (some sort of oop pattern would be nice) to "cast" a base type to a form of its derived type. 我不确定这是否是一件奇怪的事情,或者这是否是某种代码的味道...但是我想知道是否有一种方法(某种oop模式会很好)“投射”基本类型为其派生类型的形式。 I know this makes little sense as the derived type will have additional functionality that the parent doesn't offer which is in its self not fundamentally sound. 我知道这没有多大意义,因为派生类型将具有父级不提供的其他功能,这从根本上来说并不健全。 But is there some way to do this? 但是有什么方法可以做到这一点吗? Here is a code example to so I can better explain what I"m asking. 这是一个代码示例,以便我可以更好地解释我的要求。

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

I know this seems goofy but is there any way to accomplish something of this sort? 我知道这看起来很愚蠢,但是有什么办法可以完成这种事情吗?

Not soundly, in "managed" languages. 不太好用“托管”语言。 This is downcasting , and there is no sane down way to handle it, for exactly the reason you described (subclasses provide more than base classes - where does this "more" come from?). 这是垂头丧气的 ,并且没有合理的方法来处理它,这正是您所描述的原因(子类提供的不仅仅是基类-“更多”是从哪里来的?)。 If you really want a similar behaviour for a particular hierarchy, you could use constructors for derived types that will take the base type as a prototype. 如果您确实希望特定层次结构具有类似的行为,则可以将构造函数用于派生类型,这些派生类型会将基本类型作为原型。

One could build something with reflection that handled the simple cases (more specific types that have no addition state). 可以用反射来构建一些可以处理简单情况的东西(没有加法状态的更具体的类型)。 In general, just redesign to avoid the problem. 通常,只需重新设计就可以避免该问题。

Edit: Woops, can't write conversion operators between base/derived types. 编辑:糟糕,无法在基本/派生类型之间编写转换运算符。 An oddity of Microsoft trying to "protect you" against yourself. 微软试图“保护您”免受自己攻击的怪异行为。 Ah well, at least they're no where near as bad as Sun. 嗯,至少它们远没有Sun差。

Try composition instead of inheritance! 尝试组成而不是继承!

It seems to me like you'd be better off passing an instance of SomeBaseClass to the SomeDerivedClass (which will no longer derive base class, and should be renamed as such) 在我看来,最好将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);
}

Check out http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html (page 3): 查看http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html (第3页):

Code reuse via composition Composition provides an alternative way for Apple to reuse Fruit's implementation of peel(). 通过组合进行代码重用组合为Apple提供了另一种方法来重用Fruit的Peel()实现。 Instead of extending Fruit, Apple can hold a reference to a Fruit instance and define its own peel() method that simply invokes peel() on the Fruit. Apple可以扩展对Fruit实例的引用,而不必定义Fruit实例,而是定义自己的peel()方法,该方法仅对Fruit调用peel()。

Personally I don't think it's worth the hassle of using Inheritance in this case. 就我个人而言,在这种情况下使用继承不会带来麻烦。 Instead just pass the base class instance in in the constructor and access it through a member variable. 相反,只需在构造函数中传递基类实例,然后通过成员变量对其进行访问。

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

Downcasting makes sense, if you have an Object of derived class but it's referenced by a reference of base class type and for some reason You want it back to be referenced by a derived class type reference. 如果您有一个派生类的对象,但是由基类类型的引用来引用,并且出于某种原因,您希望它又被派生类类型的引用所引用,那么向下转换是有意义的。 In other words You can downcast to reverse the effect of previous upcasting. 换句话说,您可以向下转换以反转先前向上转换的效果。 But You can't have an object of base class referenced by a reference of a derived class type. 但是您不能通过派生类类型的引用来引用基类的对象。

I'm not saying I recommend this. 我并不是说我推荐这个。 But you could turn base class into JSON string and then convert it to the derived class. 但是您可以将基类转换为JSON字符串,然后将其转换为派生类。

SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject));

No, this is not possible. 不,这是不可能的。 In a managed language like C#, it just won't work. 在像C#这样的托管语言中,它将无法正常工作。 The runtime won't allow it, even if the compiler lets it through. 即使编译器允许运行时,运行时也不允许这样做。

You said yourself that this seems goofy: 您自己说这似乎很愚蠢:

SomeBaseClass class = new SomeBaseClass();
SomeDerivedClass derClass = (SomeDerivedClass)class; 

So ask yourself, is class actually an instance of SomeDerivedClass ? 所以问问自己, class实际上是SomeDerivedClass的实例吗? No, so the conversion makes no sense. 不,因此转换没有意义。 If you need to convert SomeBaseClass to SomeDerivedClass , then you should provide some kind of conversion, either a constructor or a conversion method. 如果需要将SomeBaseClass转换为SomeDerivedClass ,则应提供某种转换,构造函数或转换方法。

It sounds as if your class hierarchy needs some work, though. 听起来好像您的类层次结构需要一些工作。 In general, it shouldn't be possible to convert a base class instance into a derived class instance. 在一般情况下,它不应该是可能的一个基类实例转换成一个派生类的实例。 There should generally be data and/or functionality that do not apply to the base class. 通常应该有不适用于基类的数据和/或功能。 If the derived class functionality applies to all instances of the base class, then it should either be rolled up into the base class or pulled into a new class that is not part of the base class hierarchy. 如果派生类功能适用于基类的所有实例,则应将其汇总到基类中或拉入不属于基类层次结构的新类中。

C# language doesn't permit such operators, but you can still write them and they work: C#语言不允许使用此类运算符,但是您仍然可以编写它们,并且它们可以工作:

[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Implicit(Base a) { ... }

[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Explicit(Base a) { ... }

Yes - this is a code smell, and pretty much nails down the fact that your inheritance chain is broken. 是的-这是一种代码味道,几乎可以确定继承链已损坏的事实。

My guess (from the limited sample) is that you'd rather have DerivedClass operate on an instance of SomeBaseClass - so that "DerivedClass has a SomeBaseClass", rather than "DerivedClass is a SomeBaseClass". 我的猜测 (从有限的示例中得出)是,您希望DerivedClass在SomeBaseClass的实例上进行操作-以便“ DerivedClass 具有 SomeBaseClass”,而不是“ DerivedClass SomeBaseClass”。 This is known as "favor composition over inheritance". 这被称为“有利于继承而不是继承”。

Have you thought about an interface that what is currently your base class and your derived class both would implement? 您是否考虑过一个接口,您的基类和派生类当前都将实现什么接口? I don't know the specifics of why you're implementing this way but it might work. 我不知道您为什么要执行这种方式的细节,但它可能会起作用。

As others have noted, the casting you suggest is not really possible. 正如其他人指出的那样,您建议的转换实际上是不可能的。 Would it maybe be a case where the Decorator pattern (Head First extract) can be introduced? 可以引入Decorator模式 (Head First提取)吗?

That cannot work. 那行不通。 Go look at the help page linked by the compile error. 请转到由编译错误链接的帮助页面。

The best solution is to use factory methods here. 最好的解决方案是在此处使用工厂方法。

This is called downcasting and Seldaek's suggestion to use the "safe" version is sound. 这被称为向下转换,Seldaek关于使用“安全”版本的建议是正确的。

Here's a pretty decent description with code samples . 这是一个带有代码示例的相当不错的描述

This is not possible because how are you going to get the "extra" that the derived class has. 这是不可能的,因为您将如何获得派生类所具有的“额外”价值。 How would the compiler know that you mean derivedClass1 and not derivedClass2 when you instantiate it? 实例化实例时,编译器将如何得知您是指derivedClass1而不是derivedClass2?

I think what you are really looking for is the factory pattern or similar so you can instantiate objects without really knowing the explicit type that's being instantiate. 我认为您真正要寻找的是工厂模式或类似的模式,因此您可以实例化对象而无需真正知道要实例化的显式类型。 In your example, having the "Insert" method would be an interface that instance the factory returns implements. 在您的示例中,使用“ Insert”方法将是工厂返回实现的实例的接口。

I dont know why no one has said this and i may have miss something but you can use the as keyword and if you need to do an if statement use if. 我不知道为什么没有人这么说,我可能会错过一些东西,但是您可以使用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 -编辑- 很久以前我问这个问题

As many answers have pointed out, you can't downcast which makes total sense. 正如许多答案所指出的那样,您不能低估这很有意义。

However, in your case, SomeDerivedClass doesn't have properties that will be 'missing'. 但是,在您的情况下, SomeDerivedClass没有会“丢失”的属性。 So you could create an extension method like this: 因此,您可以创建一个扩展方法,如下所示:

public static T ToDerived<T>(this SomeBaseClass baseClass) 
    where T:SomeBaseClass, new()
{
    return new T()
    {
        BooleanEvaluator = baseClass.BooleanEvaluator,
        GetBaseClassName = baseClass.GetBaseClassName
    };
}

So you aren't casting, just converting: 因此,您无需转换,只需进行转换即可:

SomeBaseClass b = new SomeBaseClass();
SomeDerivedClass c = b.ToDerived<SomeDerivedClass>();

This only really works if all of the data in the base class is in the form of readable and writable properties. 仅当基类中的所有数据都具有可读和可写属性的形式时,这才真正起作用。

I've recently been in the need of extending a simple DTO with a derived type in order to put some more properties on it. 我最近需要用派生类型扩展一个简单的DTO,以便在其上添加更多属性。 I then wanted to reuse some conversion logic I had, from internal database types to the DTOs. 然后,我想重用一些转换逻辑,从内部数据库类型到DTO。

The way I solved it was by enforcing an empty constructor on the DTO classes, using it like this: 我解决问题的方法是在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
        }
    }
}

I can then reuse the conversion logic from the simple DTO when converting to the complex one. 然后,当转换为复杂的DTO时,我可以重用简单DTO的转换逻辑。 Of course, I will have to fill in the properties of the complex type in some other way, but with many, many properties of the simple DTO, this really simplifies things IMO. 当然,我将不得不以其他方式来填充复杂类型的属性,但是使用简单DTO的许多属性,这确实简化了IMO。

C++ handles it using a constructor. C ++使用构造函数来处理它。 C++ Typecasting . C ++类型转换 It seems like an oversight to me. 对我来说,这似乎是一个疏忽。 Many of you have brought up the issue of what would the process do with the extra properties. 你们中的许多人都提出了该过程如何处理这些额外属性的问题。 I would answer, what does the compiler do when it creates the derived class when the programmer does not set the properties? 我会回答,当程序员未设置属性时,编译器在创建派生类时会做什么? I have handled this situation similar to C++. 我已经处理了类似C ++的情况。 I create a constructor that takes the base class then manually set the properties in the constructor. 我创建一个使用基类的构造函数,然后在构造函数中手动设置属性。 This is definitely preferable to setting a variable in the derived class and breaking the inheritance. 绝对比在派生类中设置变量并破坏继承更好。 I would also choose it over a factory method because I think the resulting code would be cleaner looking. 我还会选择它而不是工厂方法,因为我认为生成的代码看起来更简洁。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM