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

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

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.

33
задан Peter Mortensen 8 April 2014 в 07:57
поделиться

5 ответов

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'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.
56
ответ дан 27 November 2019 в 17:57
поделиться

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

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

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

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

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

10
ответ дан 27 November 2019 в 17:57
поделиться

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

Обычно в этом нет необходимости. Обычно вы просто передаете поведение дочернему классу и вызываете его через интерфейс. Например, если у вас разные требования к 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;
    }
}
4
ответ дан 27 November 2019 в 17:57
поделиться
if (product is Book)
{
   ...
}
else if (product is Soda)
{
   ...
}
3
ответ дан 27 November 2019 в 17:57
поделиться

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

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

1
ответ дан 27 November 2019 в 17:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: