首页 > 解决方案 > 访问抽象类列表中的信息

问题描述

我有下面显示的代码。我创建了一个动物列表(“List< Animals >”),在其中添加了几个 Cat、Dog 和 Bird。是否可以直接访问每个类的不同字段,例如狗的年龄或猫的颜色列表?

谢谢!

public abstract class Animals
{
}


public class Cat : Animals
{
    public string Name;
    public string Color;
    public int Age;
    public string Breed  
}


public class Dog : Animals
{
    public string Name;
    public string Color;
    public int Age;
    public string Breed
}

public class Bird : Animals
{
    public string Name;
    public int Age;
    public string Wing_Color;
    public string Fly_Height;
}

标签: c#listinheritanceabstract-class

解决方案


是否可以通过该列表直接访问每个类别的不同字段,例如狗的年龄或猫的颜色?

是的,但前提是您在编译时知道类型。

C#(尚)不支持正确的代数类型或可区分联合,因此要进行详尽检查,您需要使用is运算符(最好使用ExhaustiveMatching 之类的代码分析包)或定义自己的Match方法。

像这样:

方法1:使用is运算符:

运算符的这种使用is有时被称为“模式匹配”——但我不同意这个术语,因为它实际上只是对运行时类型检查语法的人体工程学改进,而不是真正的(在 Haskell 意义上)数据模式匹配.

List<Animal> animals = ...

foreach( Animal a in animals )
{
    if( a is Cat cat )
    {
        Console.WriteLine( "Cat breed: {0}.", cat.Breed );
    }
    else if( a is Dog dog )
    {
        Console.WriteLine( "Dog breed: {0}.", dog.Breed );
    }
    else if( a is Bird bird )
    {
        Console.WriteLine( "Bird name: {0}.", bird.Name );
    }
    else
    {
        throw new InvalidOperationException( "Unknown animal subclass." );
    }
}

如果您定义了要切换的封闭类型层次结构(如 Java 样式的枚举类)(使用带有-syntax 的C# 7.x语句)但不存在,则ExhaustiveMatching之类的代码分析包会给您编译时警告或错误子类案例。switchcase TypeName name:

方法 2:使用自定义匹配/切换方法:

定义自定义MatchSwitch方法允许您要求使用调用站点是详尽无遗的,但这取决于您的Match/Switch方法是否详尽 - 但由于它将责任负担转移给您 - 而不是消费者 - 这种方法具有优势.

像这样:

abstract class Animal
{
    public TResult Match<TResult>(
        Func<Cat ,TResult> isCat,
        Func<Dog ,TResult> isDog,
        Func<Bird,TResult> isBird
    )
    {
        if     ( this is Cat  c ) return isCat( c );
        else if( this is Dog  d ) return isDog( d );
        else if( this is Bird b ) return isBird( b );
        else                      throw new InvalidOperationException( "Unknown animal subclass." );
    }
}

像这样使用:

foreach( Animal a in animals )
{
    String summary = a.Match(
        isCat : c => "Cat breed: " + c.Breed,
        isDog : d => "Dog breed: " + d.Breed,
        isBird: b => "Bird name: " + b.Name,
    );

    Console.WriteLine( summary );
}

推荐阅读