Подтвердить что ты не робот

Как получить фактический тип производного класса из его родительского интерфейса

Скажем, у нас есть такая часть кода, как это:

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

Как я могу определить, какой продукт фактически передан в метод SellThisProduct() в методе?

Я думаю, что если я скажу GetType() или что-то, это, вероятно, вернет тип IProduct.

4b9b3361

Ответ 1

GetType получает точный тип времени выполнения объекта. Из документа :

Экземпляр Type, представляющий точный тип времени выполнения текущего экземпляра.

Вы также можете использовать is, чтобы определить, является ли объект экземпляром определенного типа:

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

Зачем вам нужен точный тип времени выполнения? Вся точка интерфейса заключается в том, что вы должны скрывать детали реализации за общим интерфейсом. Если вам нужно принять действие на основе типа, это большой намек на то, что вы нарушаете инкапсуляцию, которую он предоставляет.

Один из вариантов - использовать полиморфизм:

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!"; }
}

Поскольку эти три типа вообще не связаны, наследование от общего типа не имеет смысла. Но чтобы представить, что они обладают одинаковой способностью создавать шум, мы можем использовать интерфейс IVocalizer, а затем попросить каждого сделать шум. Это гораздо более чистый подход: теперь вам не нужно заботиться о том, какой тип объекта есть, когда вы хотите попросить его сделать шум:

IVocalizer talker = new ???();  // Anything that 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.

Ответ 2

Object.GetType возвращает точный тип времени выполнения экземпляра. Это то, что вы должны использовать.

Хотя, вообще говоря, вам все равно, каков тип среды выполнения - если вы пишете код, чтобы определить это, он, вероятно, отражает ошибку в вашем проекте где-то.

Фактически, имя интерфейса IProduct уже немного запатентовано кодом. Это не так, само по себе, но интерфейсы предназначены для определения действий, доступных для конкретного объекта, то есть того, что он делает. Имя IProduct похоже, описывает, что это такое, а не то, что оно делает, что более подходит для абстрактного базового класса. Это не "правило", заметьте, но это хорошая рекомендация.

При написании методов/классов, которые зависят от абстрактного типа (базового класса или интерфейса), если вы обнаружите, что вы зависите от более производных типов или конкретных реализаций, это означает, что либо ваш абстрактный тип является анемичным (не имеют достаточную функциональность, чтобы эффективно использоваться), или ваша зависимость слишком сильно связана (зависит от деталей реализации, а не от абстракций).

Рассмотрите возможность расширения Product/IProduct, чтобы сделать больше, или чтобы ваша зависимость действительно работала над конкретными типами продуктов с помощью перегрузки методов.

Ответ 3

В то время как GetType() вернет фактический тип, вы должны использовать оператор is.

Как правило, вам не нужно это делать. Обычно то, что вы делаете, просто откладывает поведение дочернего класса и вызывает его через интерфейс. Например, если у вас разные требования к Soda vs. 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;
    }
}

Ответ 4

if (product is Book)
{
   ...
}
else if (product is Soda)
{
   ...
}

Ответ 5

typeof (product) вернет IProduct.

product.GetType() будет фактически возвращать производный тип объекта, поскольку он является функцией-членом.