C# 共変戻り値のオーバーライド

C#

共変戻り値 (Covariant return types)

C# 9.0(2019年4月リリース)から共変戻り値のオーバーライドができるようになりました。これはJavaではだいぶ昔(Java5 2004年9月リリース)から可能だったのですが、9.0以前のC#ではできませんでした。

動作としてはとても自然なので使い勝手はいいと思います。。

    public abstract class Animal 
    {
        public int Id { get; }
        public string Name { get; }

        public Animal(int id, string? name)
        {
            Id = id;
            Name = name ?? throw new ArgumentNullException(nameof(name));
        }
        public void Hello() => Console.WriteLine($"Hello {Id} {Name}"); 
    }

    public class Cat: Animal 
    {
        public Cat(int id, string name) : base(id, name) { }
    }

    public class Dog: Animal
    {
        public Dog(int id, string name) : base(id, name) { }
    }

    public abstract class AnimalHouse
    {
        public abstract IEnumerable<Animal>? Animals { get; }
        public abstract Animal? GetById(int id);
        public abstract void Add(int id, string name);
    }

    public class CatHouse : AnimalHouse
    {
        private IList<Cat> _members { get; } = new List<Cat>();
        public override IEnumerable<Cat>? Animals => _members;

        public override Cat? GetById(int id) 
        {
            return _members?.Where(m => m.Id == id).FirstOrDefault();
        }

        public override void Add(int id, string name)
        {
            _members.Add(new Cat(id, name));
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<Animal> cc = new List<Cat>();
            CatHouse catHouse = new CatHouse();
            catHouse.Add(1, "ChaTra");
            catHouse.Add(2, "Monta");
            catHouse.Add(3, "Tama");
            var check = catHouse.GetById(2);
            catHouse.GetById(2)?.Hello();
        }
    }

このコード、C# 9.0 以前では 以下のエラーでコンパイルできません。

return type must be example.Animal' to match overridden member example.AnimalHouse.GetById(int)

9.0 以前でクラスのメソッドをオーバーライドするには、戻り値の型は正確に一致す必要がありました。そこで9.0以前では、メソッドをオーバーライドせずに別のメソッドを定義して同様の動作を行う必要がありました。

public Animal? GetAnimalById(int id) 
...
public Cat? GetCatById(int id)
...

9.0以降では、メソッドの戻り値の型をオーバーライドできるようになりました。前の例では戻り値のAnimal型をオーバーライドしてCat型にしています。

ここでは、IEnumerable<Animals> を IEnumerable<Cat> でオーバーライド。Animal? GetById(int id)を Cat? GetById(int id)でオーバーライドしています。つまり、オーバーライドされるメソッド(例では Animal? GetById(int))に対して、より派生した型の戻り値(例ではCat)を宣言できます。
例ではまた、IEnumerable? Animals { get; } をIEnumerable? Cat { get; } でオーバーライドしています。読み取り専用のプロパティもより派生した型でオーバーライドできます。

参照ページ

Covariant return types - C# feature specifications
This feature specification describes covariant return types, where overriding member declarations can return a type deri...
override 修飾子 - C# リファレンス - C#
override 修飾子 - C# リファレンス
override modifier - C# reference
override modifier - C# Reference

コメント

タイトルとURLをコピーしました