5

C# でジェネリック クラスを作成する方法を学習しようとしています。このプログラムを実行するとコンパイルエラーが発生する理由を誰か説明できますか?

IZooAnimal インターフェイスを作成しました。すべての動物園の動物は、このインターフェースを実装します。

public interface IZooAnimal
{
    string Id { get; set; }
}

public class Lion : IZooAnimal
{
    string Id { get; set; }
}

public class Zebra : IZooAnimal
{
    public string Id { get; set; }
}

ZooCage には同じ種類の動物が収容されます

public class ZooCage<T> where T : IZooAnimal
{
    public IList<T> Animals { get; set; }
}

動物園のクラスにはケージがあります

public class Zoo
{
    public IList<ZooCage<IZooAnimal>> ZooCages { get; set; }
}

クラスを使用するプログラム

class Program
{
    static void Main(string[] args)
    {
        var lion = new Lion();
        var lionCage = new ZooCage<Lion>();
        lionCage.Animals = new List<Lion>();
        lionCage.Animals.Add(lion);

        var zebra = new Zebra();
        var zebraCage = new ZooCage<Zebra>();
        zebraCage.Animals = new List<Zebra>();
        zebraCage.Animals.Add(zebra);

        var zoo = new Zoo();
        zoo.ZooCages = new List<ZooCage<IZooAnimal>>();

       zoo.ZooCages.Add(lionCage);
    }
}

コンパイルすると、次のエラーが表示されます: エラー 2 引数 1: ' ConsoleApplication2.ZooCage<ConsoleApplication2.Lion>'から ' ConsoleApplication2.ZooCage<ConsoleApplication2.IZooAnimal>' に変換できません

プログラムを実行するには、どのような変更を行う必要がありますか?

4

3 に答える 3

4

@DanielMannの答えは非常に優れていますが、1つの欠点があります.元IListのインターフェースをインターフェースで使用することはできませんICage. 代わりに、ICage は ReadOnlyCollection を公開し、CageAnimal という新しいメソッドを公開する必要があります。

また、同様のアプローチを使用してコードを書き直しました。私の実装ははるかに弱いですが、内部のセマンティクスICageに固執することができます。IList

public interface IZooAnimal
{
    string Id { get; set; }
}

public class Lion : IZooAnimal
{
    public string Id { get; set; }
}

public class Zebra : IZooAnimal
{
    public string Id { get; set; }
}

public interface ICage
{
    IEnumerable<IZooAnimal> WeaklyTypedAnimals { get; }
}

public class Cage<T> : ICage where T : IZooAnimal
{
    public IList<T> Animals { get; set; }

    public IEnumerable<IZooAnimal> WeaklyTypedAnimals
    {
        get { return (IEnumerable<IZooAnimal>) Animals; }
    }
}

public class Zoo
{
    public IList<ICage> ZooCages { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var lion = new Lion();
        var lionCage = new Cage<Lion>();
        lionCage.Animals = new List<Lion>();
        lionCage.Animals.Add(lion);

        var zebra = new Zebra();
        var zebraCage = new Cage<Zebra>();
        zebraCage.Animals = new List<Zebra>();
        zebraCage.Animals.Add(zebra);

        var zoo = new Zoo();
        zoo.ZooCages = new List<ICage>();

        zoo.ZooCages.Add(lionCage);
    }
}
于 2013-08-24T18:35:42.087 に答える
3

インターフェイスを実装する具象型ではなく、インターフェイスを使用してリストを定義する必要があります。

    var lionCage = new ZooCage<IZooAnimal>();
    lionCage.Animals = new List<IZooAnimal>();

その後、コードは期待どおりに機能します。

具体的な型を一般化された型に変換することは許可されていないため、最初のコードは機能しませんでした (@default.kramer がcovariance と contravariance を指摘したように)。

私が思いついた解決策は次のとおりです。

// your ZooCage is still generic
public class ZooCage<T>
{   
    // but you declare on creation which type you want to contain only!
    private Type cageType = null;
    public ZooCage(Type iMayContain)
    {
        cageType = iMayContain;
        animals = new List<T>();
    }
    // check on add if the types are compatible
    public void Add(T animal)
    {
        if (animal.GetType() != cageType)
        {
            throw new Exception("Sorry - no matching types! I may contain only " + cageType.ToString());
        }
        animals.Add(animal);
    }
    // should be generic but not visible to outher world!
    private IList<T> animals { get; set; }
}

このコードを使用すると、次のことができます。

    var lion = new Lion();
    var lionCage = new ZooCage<IZooAnimal>(typeof(Lion));
    lionCage.Add(lion);

    var zebra = new Zebra();
    var zebraCage = new ZooCage<IZooAnimal>(typeof(Zebra));
    zebraCage.Add(zebra);

ただし、次の場合にエラーがスローされます。

    zebraCage.Add(lion);

これで、動物園は安全に拡張できます。

于 2013-08-24T17:25:14.353 に答える
2

複数のケージを用意したいのですが、各タイプのケージに収容できる動物は 1 匹だけなので、モデルは少しずれています。

次のようにコードを書き直しました。

  • IZooAnimal変更されていません。
  • ICageの任意のタイプを受け入れる共変インターフェイスがありますIZooAnimal。これにより、あらゆる種類の動物に対して厳密に型指定されたケージを持つことができます。
  • 次に、のCage具体的な実装がありICageます。Cageは一般的ですが、抽象クラスにして動物固有のケージ実装を作成することも簡単にできます。たとえば、シマウマに草を与える必要があり、ライオンに肉を与える必要がある場合、ケージの実装を特殊化できます。

完全なコードは次のとおりです。

public interface IZooAnimal
{
    string Id { get; set; }
}

public interface ICage<out T> where T : IZooAnimal
{
    IReadOnlyCollection<T> Animals { get; }
}

public class Cage<T> : ICage<T> where T: IZooAnimal
{
    private readonly List<T> animals = new List<T>();

    public IReadOnlyCollection<T> Animals
    {
        get
        {
            return animals.AsReadOnly();
        }
    }

    public void CageAnimal(T animal)
    {
        animals.Add(animal);
    }
}

public class Lion : IZooAnimal
{
    public string Id { get; set; }
}

public class Zebra : IZooAnimal
{
    public string Id { get; set; }
}

public class Zoo
{
    public IList<ICage<IZooAnimal>> Cages { get; set; }
}

internal class Program
{

    private static void Main(string[] args)
    {
        var lion = new Lion();
        var zebra = new Zebra();
        var lionCage = new Cage<Lion>();
        lionCage.CageAnimal(lion);

        var zebraCage = new Cage<Zebra>();
        zebraCage.CageAnimal(zebra);

        var zoo = new Zoo();
        zoo.Cages.Add(lionCage);
        zoo.Cages.Add(zebraCage);

    }
}
于 2013-08-24T18:28:48.467 に答える