繁体   English   中英

继承的通用类型统一

[英]Inherited Generic Type Unification

对于这样的场景:

public interface IAnimal
{

}

public interface IGiraffe : IAnimal
{

}

public interface IQuestionableCollection : IEnumerable<IAnimal>
{
    void SomeAction();
}

public interface IQuestionableCollection<out T> : IQuestionableCollection, IEnumerable<T>
    where T : IAnimal
{

}

public class QuestionableCollection<T> : IQuestionableCollection<T>
    where T:IAnimal
{
    // Implementation... 
}

编译器将生成错误:

'IQuestionableCollection<T>' cannot implement both 'System.Collections.Generic.IEnumerable<IAnimal>' and 'System.Collections.Generic.IEnumerable<T>' because they may unify for some type parameter substitutions

这是有道理的,C#无法解决的两个接口之间确实存在歧义,除非它使用类型约束,而不是像@ericlippert 在这里解释的语言规范那样。

我的问题是我应该如何在这里实现同样的效果呢?

似乎我应该能够表达该集合对于基本接口是可枚举的。 (我想提供一组可以在不知道具体类型的情况下使用的方法,以及它使一些API /反射代码更清晰,所以我想将基本集合保持为非通用的,如果有的话可能。否则,就不需要两个接口。)

我能想到的唯一可编程实现类似于:

public interface IQuestionableCollectionBase
{
    void SomeAction();
}

public interface IQuestionableCollection : IQuestionableCollectionBase, IEnumerable<IAnimal>
{

}

public interface IQuestionableCollection<out T> : IQuestionableCollectionBase, IEnumerable<T>
    where T : IAnimal
{

}

public class QuestionableCollectionBase<T> : IQuestionableCollection
    where T : IAnimal
{
    protected List<T> _items = new List<T>();

    public void SomeAction() { }

    IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); }
    IEnumerator<IAnimal> IEnumerable<IAnimal>.GetEnumerator() { return ((IEnumerable<IAnimal>)_items).GetEnumerator(); }
}

public class QuestionableCollection<T> : QuestionableCollectionBase<T>, IQuestionableCollection<T>
    where T : IAnimal
{
    public IEnumerator<T> GetEnumerator() { return ((IEnumerable<T>)_items).GetEnumerator(); }
}

请注意,我必须将我想要在两个接口上使用的任何方法移动到基本方法,并为类本身提供两个级别的实现 - 这似乎我在这里跳过了足够的箍,我已经有了失踪的东西......

该如何实施?

最简单的解决方法是将IEnumerables从“is-a”更改为“has-a”,如下所示:

public interface IAnimal { }
public interface IGiraffe : IAnimal { }

public interface IQuestionableCollection
{
    IEnumerable<IAnimal> Animals { get; }
    void SomeAction();
}

public interface IQuestionableCollection<out T> : IQuestionableCollection
    where T : IAnimal
{
    new IEnumerable<T> Animals { get; }
}

public class QuestionableCollection<T> : IQuestionableCollection<T>
    where T : IAnimal, new()
{
    private readonly List<T> list = new List<T>();

    public IEnumerable<T> Animals
    {
        get { return list; }
    }

    IEnumerable<IAnimal> IQuestionableCollection.Animals
    {
        get { return (IEnumerable<IAnimal>)list; }
    }

    public void SomeAction()
    {
        list.Add(new T());
    }
}

class Giraffe : IGiraffe { }

[TestMethod]
public void test()
{
    var c = new QuestionableCollection<Giraffe>();
    IQuestionableCollection<Giraffe> i = c;
    IQuestionableCollection<IGiraffe> i2 = i;

    Assert.AreEqual(0, c.Animals.Count());
    Assert.AreEqual(0, i.Animals.Count());
    c.SomeAction();
    i.SomeAction();
    Assert.AreEqual(2, c.Animals.Count());
    Assert.AreEqual(2, i.Animals.Count());
}

请注意,如果添加where T : class约束,则可以避免在QuestionableCollection<T>强制转换。

将IQuestionableCollection更改为非通用IEnumerable会对编译器问题进行排序。

public interface IQuestionableCollection : IEnumerable {...}

我已经看到MS在他们的集合中使用这种模式,使用IEnumerable的非泛型版本和使用IEnumerable<T>的泛型版本。

或者,使其他IEnumerable<IAnimal>也会停止编译器错误,但这意味着在枚举时会返回IAnimals而不是T。

你可以试试这个:

public interface IAnimal
{

}

public interface IGiraffe : IAnimal
{

}

public interface IQuestionableCollection<T> : IEnumerable<T> where T : IAnimal
{
    void SomeAction();
}

public interface IQuestionableCollection : IQuestionableCollection<IAnimal>
{

}

public class QuestionableCollection<T> : IQuestionableCollection<T>, IEnumerable<T>
    where T : IAnimal
{
    public void SomeAction() { }

    public IEnumerator<T> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

鉴于语言的限制,您将无法解决IQuestionableCollection和IQuestionableCollection都实现通用接口的问题。

实质上,您指定的是IQuestionableCollection实现两个可能的列表,List和另一个List。 这里没有等级关系,因此无法解决。

话虽这么说,你必须用IQuestionableCollection上的IEnumerable替换IEnumerable,以便为编译器提供一个继承链。 实际上,除非你计划实现一个vanilla QuestionableCollection,否则实际上甚至没有必要声明基础IQuestionableCollection。

在我更新的代码中,我把它带到了QuestionableCollection的消费者,以说明QuestionableCollection()的枚举保留了IGiraffe的输入。

    public interface IAnimal {}

    public interface IGiraffe : IAnimal { }

    public interface IQuestionableCollection : IEnumerable
    {
        void SomeAction();
    }

    public interface IQuestionableCollection<out T> : IQuestionableCollection, IEnumerable<T>
     where T : IAnimal
    { }

    public class QuestionableCollection<T> : IQuestionableCollection<T>
        where T : IAnimal
    {

        private List<T> list = new List<T>(); 

        public IEnumerator<T> GetEnumerator()
        {
            return list.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public void SomeAction()
        {
            throw new NotImplementedException();
        }
    }

      class Program
    {
        static void Main(string[] args)
        {
            var questionable = new QuestionableCollection<IGiraffe>();
            foreach (IGiraffe giraffe in questionable)
            {

            }
        }
    }

对于非通用实现

1你总是可以安全地向上翻

var questionable = new QuestionableCollection();
IEnumerabl<IAnimal> animals = questionable.OfType<IAnimal>();

-要么-

2您可以让NonGeneric QuestionableCollection类实现IEnumerable

 public class QuestionableCollection : IQuestionableCollection, IEnumerable<IAnimal> 
{
    public IEnumerator<IAnimal> GetEnumerator()
    {
        var l = new List<Giraffe>();
        l.Add(new Giraffe());
        l.Add(new Giraffe());
        return l.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void SomeAction()
    {
        throw new NotImplementedException();
    }
}

然后,您可以在没有强制转换操作的情况下枚举。

    var questionable = new QuestionableCollection();
    foreach (IAnimal giraffe in questionable)
    {
        var i = giraffe;
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM