2012-01-30 1 views
3

Учитывая этот фрагмент кода, который может быть легко вставлен в LINQPad (или слегка измененный в растворе Visual Studio консоли):Как сказать, какой интерфейс возвращается метод

void Main() 
{ 
    var cat = this.GetCat(); 
    var dog = this.GetDog(); 
    cat.Think(); 
    cat.ThinkHarder(); 
    //dog.Think(); // Does not compile. 
    //dog.ThinkHarder(); // Does not compile. 

    //if ([dog is returned as ISmartAnimal]) // What to put here? 
     ((ISmartAnimal)dog).Think(); // Compiles, runs, but shouldn't. 

    reportTypeProperties(cat); 
    reportTypeProperties(dog); 
} 

interface IAnimal 
{ 
    string Name { get; set; } 
} 

interface ISmartAnimal : IAnimal 
{ 
    void Think(); 
} 

class Animal : IAnimal, ISmartAnimal 
{ 
    public string Name { get; set; } 
    public void Think() { } 
} 

ISmartAnimal GetCat() 
{ 
    return new Animal(); 
} 

IAnimal GetDog() 
{ 
    return new Animal(); 
} 

static void reportTypeProperties(object obj) 
{ 
    var type = obj.GetType(); 
    Console.WriteLine("Type: {0}", type.Name); 
    Console.WriteLine("Is smart? {0}", obj is ISmartAnimal); 
} 

static class ext 
{ 
    public static void ThinkHarder(this ISmartAnimal animal) 
    { } 
} 

Выход reportTypeProperties показывает, что dog , хотя возвращается как IAnimal, «is» является ISmartAnimal. (То же самое для обоих объектов)

Тип: Животное
умна? True

Это потому, что GetType() возвращает конкретный тип объекта, а не его текущий интерфейс.

Мой вопрос. Есть ли способ сказать, что dog возвращается как IAnimal? (см. Псевдокод). Компилятор знает (так же как и quickview). Предположим, у меня был какой-то объект для животных, и я хотел проверить в коде времени выполнения, могу ли я сделать это Think().

Предпосылки:
Это может показаться академическим занятием. И может показаться странным, что класс (Animal) реализует интерфейс (ISmartAnimal), который вы не хотите показывать всегда. Но я спрашиваю, потому что я встретил нечто подобное в Entity Framework. Если вы хотите, вы можете прочитать об этом here, но он отвлекается на функции EF. Если вы не хотите вникать в это, достаточно сказать, что необходимо, чтобы Animal реализовали оба интерфейса.


Отказ от ответственности:
«Любое сходство с реальными животными чисто случайно :)»

ответ

4

Это звучит, как вы заинтересованы в типе компиляции в dog переменной. Вы можете рода из этого получить, сделав ReportTypeProperties родовое и позволяя компилятору определить тип, основанный на тип переменной:

static void ReportTypeProperties<T>(T obj) 
{ 
    Console.WriteLine("Compile-time type: {0}", typeof(T).Name); 
    Console.WriteLine("Actual type: {0}", obj.GetType().Name); 
    Console.WriteLine("Is smart? {0}", obj is ISmartAnimal); 
} 

Заметим, что это может быть манипуляциям различными способами, например,

object dog = GetDog(); 
ReportTypeProperties(dog); // Would show as object 

или

IAnimal dog = GetDog(); 
ReportTypeProperties<object>(dog); // Would show as object 

Это не совсем ясно, что большая картина здесь - он чувствует, вряд ли мне, что происходит в этом направлении будет приводить к хорошему дизайну.

+0

Спасибо (еще раз) Джон. Это не дизайн, который я хотел бы реализовать слишком часто, но я был немного удивлен тем, что ObjectQuery (и DataQuery от EF в linq-to-sql) EF выполняет то же самое с IQueryable и IOrderedQueryable. Хотя я уверен, что есть веские причины для этого. –

Смежные вопросы