2016-07-17 3 views
1

У меня есть абстрактный класс Animal, в котором хранятся некоторые общие поля, например имя, здоровье. У меня есть несколько классов животных, например, Tiger, но у меня также есть класс Fish, у которого есть дополнительное поле, а другие классы животных не имеют, canSplash.Доступ к свойствам дочернего класса из списка объектов абстрактного класса

У меня есть список объектов Animal. Я могу получить доступ к общим полям, но я не могу получить доступ к полю canSplash для Fish. Я ищу помощь для доступа к конкретным классам конкретных полей из абстрактного класса.

class Zoo 
{ 
    public List<Animal> animals = new List<Animal>(); 

    public Zoo() 
    { 
     animals.Add(new Monkey()); 
     animals.Add(new Tiger()); 
     animals.Add(new Fish()); 
    } 

    public static void displayZooPopulation() 
    { 
     foreach (var a in animals) 
     { 
      if (a.species == "fish" && a.CanSplash) 
      { 
       Console.WriteLine("{0} can splash",a.Name); 
      } 
     } 
    } 
} 

class Fish : Animal { 
    private bool canSplash 
    public bool CanSplash { get; set; } 
} 
+0

Почему is canSplash bool при поплавке CanSplash? Почему canSplash личный? – jdweng

+0

canSplash является закрытым, потому что вы используете get и set для доступа к нему. Извините, плавать было ошибкой, я набрал этот вопрос. – ecclesm

+2

удалите поле: private bool canSplash, вам это не нужно. –

ответ

3

Простой ответ будет, проверьте тип, безопасно бросить его и проверить, если он не null:

var fish = a as Fish; 
if (fish != null && fish.CanSplash) 
{ 
    Console.WriteLine("{0} can splash",a.Name); 
} 

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

Лучший подход заключается в использовании интерфейс для таких вещей, как ISplashable:

public interface ISplashable 
{ 
    bool CanSplash { get; } 
} 

Теперь реализовать этот интерфейс во всех ваших дочерних классов, которые должны быть в состоянии брызнуть:

public class Fish : Animal, ISplashable 
{ 
    // ... 

    public bool CanSplash { get; set; } // this also implements CanSplash { get; } 

    // ... 
} 

public class Elephant : Animal, ISplashable 
{ 
    // ... 

    public bool CanSplash { get { return true; } } 

    // ... 
} 

сейчас вы можете проверить этот интерфейс вместо конкретного класса:

var splasher = a as ISplashable; 
if (splasher != null && splasher.CanSplash) 
{ 
    Console.WriteLine("{0} can splash",a.Name); 
} 
+0

Спасибо, отлично работал. – ecclesm

+0

Wow спасибо за расширенный ответ с интерфейсами, это не только очень полезно в этом случае, но и значительно упростило использование интерфейсов. Большое спасибо! – ecclesm

0

// удалить статическое ключевое слово, потому что вы не можете получить доступ к животным (или животные должны быть статическими)

проверки типа а, и принять ваши действия

Метод может быть:

public void displayZooPopulation() 
    { 
     foreach (var a in animals) 
     { 
      if (a is Fish) 
      { 
//here sure "a" is not null, no need to check against null 
       var fish = a as Fish; 
       // if (a.species == "fish" && (Fish) a.CanSplash) 
       if (fish.CanSplash) 
       { 
        Console.WriteLine("{0} can splash", a.Name); 
       } 
      } 
     } 
    } 

Кстати, вы говорите, что Animal является абстрактным классом, где реализуется абстрактные методы в классе рыбы :)

+0

В этом случае вы проверяете тип два раза, что необязательно. Ваш подход касается старого шаблона 'if (a is SomeClass) {var c = (SomeClass) a; ... ', что является неэффективным. – abto

+0

Я сосредоточен в области реализации ОП, проверяю тип только один раз (не дважды), чтобы предпринять необходимые действия. Ваша модификация с использованием интерфейса хороша для большой сложной модели, для простой модели класса вам не нужно. BTW, в C# 6, для проверки на null мы можем использовать: if (splasher? .CanSplash) :) –

+0

Ваша «концентрация» - это именно то, что делает безопасный тип.Оператор 'if (a is Fish)' не нужен, особенно если вы хотите использовать новую функцию C# 6: 'var fish = a as Fish; if (fish? .CanSplash) ... '. И вы правы, интерфейсы не нужны в простой конструкции, но я сказал это в своем ответе ... – abto

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