2012-05-08 3 views
2

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

Вот что у меня есть:

public class Animal {} 
public class Cat : Animal {} 

public interface IPetSitter {} 
public interface IPetSitter<T> IPetSitter where T : Animal { 
    T Pet { get; set; } 
} 

public class Kid { } 

public class NeighborhoodKid : Kid, IPetSitter<Animal> { 
    Animal Pet { get; set; } 
} 

// --- Implementation --- 

// Kid Timmy is instantiated elsewhere 
// Animal type "A" is passed in dynamically 

if (Timmy is IPetSitter) { 
    ((IPetSitter<A>)Timmy).Pet = new A(); 
} 

Этот бросок будет ошибка, если типы не совпадают. Я хотел бы сделать что-то вроде этого:

public interface IPetSitter { 
    object Pet { get; set; } 
} 

public interface IPetSitter<T> : IPetSitter where T : Animal { 
    new T Pet { get; set; } 
} 

// --- Implementation --- 

NeighborhoodKid Timmy = new NeighborhoodKid(); 
((IPetSitter)Timmy).Pet = new Cat(); 

Но что заставляет что-либо реализующий IPetSitter иметь как [объект Pet] и [Cat Pet] свойства.

Буду признателен за любые идеи. Благодарю.

UPDATE: я должен был бы сделать его более ясным на начальном этапе, но иногда я буду создавать Kid класс, а иногда NeighborhoodKid класс. Вот почему мне нужно бросить IPetSitter<T>. Не все дети, которых я создаю, будут сидеть с домашними животными. Это начинает звучать жуткий.

+3

Почему бы не просто 'Timmy.Pet = new Cat();'? – SwDevMan81

+0

Это не сработает, потому что 'NeighborhoodKid' не обязательно имеет свойство« Pet ». Вам нужно указать его как своего рода «IPetSitter », чтобы его получить. – Montlebalm

ответ

0

Timmy, в конечном счете, был инициализирован как NeighborhoodKid. Это означает, что Pet, для него, как Животное. Тимми - это IPetSitter<Animal>, и вы не можете наложить его на IPetSitter<Cat>.

Вы могли бы сделать это наоборот, хотя, предполагая Cat: Animal.

Это:

((IPetSitter<Animal>)Timmy).Pet = new Cat(); 

На самом деле работает просто потому, что Тимми действительно IPetSitter<Animal>, так как NeighborhoodKid: IPetSitter<Animal>, так что вы ничего не делает с этим броском - просто получить доступ к собственности для домашних животных.

Проблема с линией после этого, не имеет доступа к Pet и не помещает Cat в нее - это литье Timmy на IPetSitter<Cat>, что является проблемой. Вы удручаете его тем, чего нет.

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

Если вы хотите NeighborhoodKid быть IPetSitter любого вида животных, включая животное себя, вы должны сделать:

public class NeighborhoodKid<T> : IPetSitter<T> where T : Animal 
{ 
    ... 
} 

Таким образом, он является общим, и ограничивает его к нечто, что является либо Животное или что-то, что происходит от Животного, прямо или косвенно.

Тем не менее, если вы инициализируются как new NeighborhoodKid<Animal>(), вы не сможете смотреть на него, как (он же бросил его в) IPetSitter<Cat>, потому что он был инициализирован как IPetSitter<Animal> (так как общий параметр T данного конструктору NeighborhoodKid был Animal, и передается в общий параметр IPetSitter).

+0

Я знаю, как это исправить, если я жестко кодировал классы, но у меня есть только «Cat» во время выполнения. На самом деле, я просто хочу получить доступ к свойству «Pet», но у меня возникают проблемы с кастом. – Montlebalm

+0

Но у 'Тимми' нет собственности' Pet'! Он имеет свойство «Животное»! – SimpleVar

+0

Все это происходит динамически на основе типов, указанных в другом месте. Некоторое время я буду создавать экземпляр «NeighborhoodKid», который не является «PetSitter ». Я хочу установить свойство «Pet», когда это применимо, но игнорировать его, если нет. Я должен был сделать это более ясным. Я обновил исходный вопрос более подробно. – Montlebalm

0

Проблема заключается в том, что вы определили

public class NeighborhoodKid : IPetSitter<Animal> 
{ 
    Animal IPetSitter<Animal>.Pet { get; set; } 
} 

и не

public class NeighborhoodKid : IPetSitter<Cat> 
{ 
    Cat IPetSitter<Animal>.Pet { get; set; } 
} 

или

public class NeighborhoodKid<T> : IPetSitter<T> where T : Animal 
{ 
    Cat IPetSitter<T>.Pet { get; set; } 
} 
0

Почему просто не Timmy.Pet = new Cat();? Просто сделайте это public и вы будете все готово:

public class NeighborhoodKid : Kid, IPetSitter<Animal> 
{ 
    public Animal Pet { get; set; } 
} 

Если вы создаете NeighborhoodKid то не наследуют от IPetSitter, связующий обыкновение быть Availabe.

public class LazyNeighborhoodKid : Kid 
{ 
    // Nothing here, hes not a Pet Sitter, can access Pet 
} 
0

Я не являюсь поклонником дженериков вне их полезности в коллекциях, и именно поэтому. Вы заставляете каждого NeighborhoodKid привязываться к одному конкретному типу Animal. Что, если Тимми сможет наблюдать за кошками или собаками? Собираетесь ли вы создавать разные экземпляры Timmy для каждого?

Вместо этого я думаю, что вы применяете типы животных на уровне экземпляра. Например (я усечен некоторых типов для краткости):

public interface IAnimal {...} 

public class Cat : IAnimal {...} 

public interface IPetSitter 
{ 
    IAnimal Pet { get; set; } 
} 

public class Kid : IPetSitter 
{ 
    public Kid (params Type[] allowedPets) { 
     _allowedPets = allowedPets; 
    } 

    readonly IEnumerable<Type> _allowedPets; 

    IAnimal _pet; 
    public IAnimal Pet 
    { 
     get { 
      return _pet; 
     } 
     set { 
      if (!_allowedPets.Contains(value.GetType()) { 
       throw new InvalidArgumentException("This instance does not support " + value.GetType().Name + "."); 
      } 
      _pet = value; 
     } 
    } 
} 

Если оставить исполнение на уровне экземпляра, то вам не обязательно нужно использовать бетонную отливку просто установить имущество.