简体   繁体   English

如何从其父接口获取派生类的实际类型

[英]How to get actual type of an derived class from its parent interface

Let's say we have a code portion like this: 假设我们有一个像这样的代码部分:

IProduct product = ProductCreator.CreateProduct(); //Factory method we have here
SellThisProduct(product);

//...

private void SellThisProduct(IProduct product)
{
  //.. Do something here
}

//...

internal class Soda : IProduct
{}

internal class Book : IProduct
{}

How can I infer which product is actually passed into SellThisProduct() method in the method? 如何推断哪个产品实际传递到方法中的SellThisProduct()方法?

I think if I say GetType() or something it will probably return the IProduct type. 我想如果我说GetType()或者它可能会返回IProduct类型。

GetType gets you the exact runtime type of an object. GetType您提供对象的确切运行时类型。 From the documentation : 文档

The Type instance that represents the exact runtime type of the current instance. Type实例,表示当前实例的确切运行时类型。

You can also use is to determine if an object is an instance of a specific type: 您还可以使用is来确定对象是否是特定类型的实例:

var noise = (obj is Velociraptor) ? "SKREEE!" : "<unknown>";

Why do you need the exact runtime type, though? 但是,为什么需要确切的运行时类型? The entire point of an interface is that you should be hiding the implementation details behind the common interface. 接口的全部内容是您应该隐藏公共接口背后的实现细节。 If you need to take an action based on the type, that's a big hint that you're violating the encapsulation it provides. 如果你需要根据类型采取行动,这是一个很大的暗示,你违反了它提供的封装。

One alternative is to use polymorphism: 一种替代方法是使用多态:

public interface IVocalizer { string Talk(); }

public class Doorbell : IVocalizer {
  public string Talk() { return "Ding-dong!" }
}
public class Pokemon : IVocalizer {
  public string Talk() {
    var name = this.GetType().ToString();
    return (name + ", " + name + "!").ToUpper(); } // e.g., "PIKACHU, PIKACHU!"
}
public class Human : IVocalizer {
  public string Talk() { return "Hello!"; }
}

Since these three types aren't related at all, inheritance from a common type doesn't make sense. 由于这三种类型根本不相关,因此从常见类型继承是没有意义的。 But to represent that they share the same capability of making noise, we can use the IVocalizer interface, and then ask each one to make a noise. 但是为了表示它们具有相同的发出噪声的能力,我们可以使用IVocalizer接口,然后让每个人发出噪音。 This is a much cleaner approach: now you don't need to care what type the object is when you want to ask it to make a noise: 这是一种更清洁的方法:现在,当您想要让它产生噪音时,您无需关心对象的类型:

IVocalizer talker = new ???();  // Anything that's an IVocalizer can go here.

// elsewhere:
Console.WriteLine(talker.Talk());    // <-- Now it doesn't matter what the actual type is!
                                     //   This will work with any IVocalizer and you don't
                                     //   need to know the details.

Object.GetType returns the exact runtime type of the instance. Object.GetType返回实例的确切运行时类型 That is what you should use. 这就是你应该使用的。

Although, generally speaking, you shouldn't care at all what the runtime type of an interface is - if you're writing code to determine that, it probably reflects an error in your design somewhere. 虽然,一般来说,你根本不应该关心接口的运行时类型 - 如果你正在编写代码来确定它,它可能反映了你的设计中的某个错误。

In fact, the interface name IProduct is already a bit of a code smell. 实际上,接口名称IProduct已经有点代码味道了。 It's not wrong, per se, but interfaces are meant to define the actions available on a particular object, ie what it does . 这本身并没有错,但接口意味着定义特定对象可用的操作 ,即它的作用 The name IProduct seems to be describing what it is , not what it does , which is more appropriate for an abstract base class. IProduct这个名称似乎在描述它什么,而不是它的作用 ,它更适合抽象基类。 This isn't a "rule", mind you, but it's a good guideline to follow. 请注意,这不是一个“规则”,但它是一个很好的指导方针。

When writing methods/classes that depend on an abstract type (base class or interface), if you find that you're depending on the more derived types or specific implementations, it means that either your abstract type is anemic (does not have enough functionality to be used effectively), or your dependency has too much coupling (depends on implementation details instead of abstractions). 在编写依赖于抽象类型(基类或接口)的方法/类时,如果发现依赖于更多派生类型或特定实现,则意味着您的抽象类型是贫血的(没有足够的功能)要有效地使用),或者你的依赖关系有太多耦合(取决于实现细节而不是抽象)。

Consider expanding the Product / IProduct to do more, or make your dependency actually work on specific product types through method overloading. 考虑扩展Product / IProduct以执行更多操作,或通过方法重载使您的依赖实际上对特定产品类型起作用。

While GetType() will return the actual type, you should use the is operator. 虽然GetType()将返回实际类型,但您应该使用is运算符。

Typically, you shouldn't need to do this. 通常,您不需要这样做。 Normally, what you do is simply defer the behavior to the child class and invoke it via the interface. 通常,您所做的只是将行为推迟到子类并通过接口调用它。 For example, if you have different requirements for Soda vs. Book (say, Soda requires collecting tax, whereas Book doesn't), then you'd create a Sell method on the interface and then in your SellThisProduct() method, you'd simply call the Sell() method on the object. 例如,如果您对Soda与Book有不同的要求(例如,Soda需要征税,而Book不需要),那么您将在界面上创建一个Sell方法,然后在SellThisProduct()方法中创建, d只需在对象上调用Sell()方法即可。

public interface IProduct
{
   public decimal Sell(); // computes price
   ...
}

..... .....

IProduct product = ProductCreator.CreateProduct(); //Factory Method we have here
SellThisProduct(product);

//...

private void SellThisProduct(IProduct product)
{
   var price = product.Sell();
   ...
}

internal class Soda : IProduct
{
   public decimal Sell()
   {
       this.Price + TaxService.ComputeTax( this.Price );
   }
}

internal class Book : IProduct
{
    public decimal Sell()
    {
         return this.Price;
    }
}
if (product is Book)
{
   ...
}
else if (product is Soda)
{
   ...
}

typeof(product) will return IProduct. typeof(产品)将返回IProduct。

product.GetType() will actually return the derived type of the object since it's a member function. product.GetType()实际上将返回对象的派生类型,因为它是一个成员函数。

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

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