C# 通用和非通用接口及继承-访问时的歧义

C# 通用和非通用接口及继承-访问时的歧义,c#,generics,inheritance,C#,Generics,Inheritance,我有以下设置 基类 public class Fruit { } 继承类 public class Apple : Fruit { } 通用基接口 public interface IFruitsBase<T> where T : Fruit { T GetItem(); void ProcessItem(T fruit); void Check(); } 我的目标是,如果有人通过以下方式访问对象: var context = new Cont

我有以下设置

  • 基类

    public class Fruit
    {
    
    }
    
  • 继承类

    public class Apple : Fruit
    {
    
    }
    
  • 通用基接口

    public interface IFruitsBase<T> where T : Fruit
    {
        T GetItem();
        void ProcessItem(T fruit);
        void Check();
    }
    
    我的目标是,如果有人通过以下方式访问对象:

    var context = new Context();
    context.Apples.[only apple relevant methods should be accessable]
    
    如果有人通过以下方式访问它:

    var context = new Context();
    context.GetFruits(item.GetType()).[only fruit relevant methods should be accessable]
    
    无论是通过方法a还是方法b得到的,都应该可以通过签名为的方法

    void Foo(IFruits fruits);
    

    在我看来,您的
    公共接口IFruits:IFruitsBase
    是错误的。您不会基于泛型接口声明非泛型接口,而是以另一种方式进行声明。这样做更有意义

    这就是我想到的:

    public interface IFruitsBase<T> where T : Fruit
    {
        T GetItem();
        void ProcessItem(T fruit);
    }
    
    public interface IFruits
    {
        Fruit GetItem();
        void ProcessItem(Fruit fruit);
    
        void Check();
    }
    
    public class Fruits<T> : IFruitsBase<T>, IFruits where T : Fruit
    {
        public T GetItem()
        {
            return null;
        }
        public void ProcessItem(T fruit)
        {
    
        }
        public void Check()
        {
    
        }
    
        Fruit IFruits.GetItem()
        {
            throw new NotImplementedException();
        }
        void IFruits.ProcessItem(Fruit fruit)
        {
            ProcessItem((T)fruit);
        }
    }
    
    // no changes from here on
    
    public class Apples : Fruits<Apple>, IApples
    {
        public void MakeAppleJuice(IEnumerable<Apple> apples)
        {
    
        }
    }
    
    public class Fruit
    {
    
    }
    
    public class Apple : Fruit
    {
    
    }
    
    public interface IApples : IFruitsBase<Apple>, IFruits
    {
        void MakeAppleJuice(IEnumerable<Apple> apples);
    }
    
    公共接口IFruitsBase,其中T:Fruit
    {
    T GetItem();
    无效处理项目(T水果);
    }
    公共接口IFruits
    {
    水果项目();
    无效加工项目(水果);
    无效检查();
    }
    公共类水果:IFruitsBase,IFruits其中T:水果
    {
    公共T GetItem()
    {
    返回null;
    }
    公共物品(T水果)
    {
    }
    公共作废检查()
    {
    }
    水果IFruits.GetItem()
    {
    抛出新的NotImplementedException();
    }
    无效IFruits.ProcessItem(水果)
    {
    加工项目((T)水果);
    }
    }
    //从现在开始没有变化
    公共类苹果:水果、苹果
    {
    public void MakeAppleJuice(IEnumerable apples)
    {
    }
    }
    公共级水果
    {
    }
    公营苹果:水果
    {
    }
    公共接口IApples:IFruitsBase、IFruits
    {
    void MakeAppleJuice(可数苹果);
    }
    
    在我看来,您的
    公共界面IFruits:IFruitsBase
    是错误的。您不会基于泛型接口声明非泛型接口,而是以另一种方式进行声明。这样做更有意义

    这就是我想到的:

    public interface IFruitsBase<T> where T : Fruit
    {
        T GetItem();
        void ProcessItem(T fruit);
    }
    
    public interface IFruits
    {
        Fruit GetItem();
        void ProcessItem(Fruit fruit);
    
        void Check();
    }
    
    public class Fruits<T> : IFruitsBase<T>, IFruits where T : Fruit
    {
        public T GetItem()
        {
            return null;
        }
        public void ProcessItem(T fruit)
        {
    
        }
        public void Check()
        {
    
        }
    
        Fruit IFruits.GetItem()
        {
            throw new NotImplementedException();
        }
        void IFruits.ProcessItem(Fruit fruit)
        {
            ProcessItem((T)fruit);
        }
    }
    
    // no changes from here on
    
    public class Apples : Fruits<Apple>, IApples
    {
        public void MakeAppleJuice(IEnumerable<Apple> apples)
        {
    
        }
    }
    
    public class Fruit
    {
    
    }
    
    public class Apple : Fruit
    {
    
    }
    
    public interface IApples : IFruitsBase<Apple>, IFruits
    {
        void MakeAppleJuice(IEnumerable<Apple> apples);
    }
    
    公共接口IFruitsBase,其中T:Fruit
    {
    T GetItem();
    无效处理项目(T水果);
    }
    公共接口IFruits
    {
    水果项目();
    无效加工项目(水果);
    无效检查();
    }
    公共类水果:IFruitsBase,IFruits其中T:水果
    {
    公共T GetItem()
    {
    返回null;
    }
    公共物品(T水果)
    {
    }
    公共作废检查()
    {
    }
    水果IFruits.GetItem()
    {
    抛出新的NotImplementedException();
    }
    无效IFruits.ProcessItem(水果)
    {
    加工项目((T)水果);
    }
    }
    //从现在开始没有变化
    公共类苹果:水果、苹果
    {
    public void MakeAppleJuice(IEnumerable apples)
    {
    }
    }
    公共级水果
    {
    }
    公营苹果:水果
    {
    }
    公共接口IApples:IFruitsBase、IFruits
    {
    void MakeAppleJuice(可数苹果);
    }
    
    我认为,如果您将这里要做的事情解包,就会出现一个解决方案

    首先,我不喜欢用复数类来表示列表。i、 e

    Apple
    Apples : List<Apple>
    

    我想,如果你把你想做的事情拆开,一个解决方案就会出现

    首先,我不喜欢用复数类来表示列表。i、 e

    Apple
    Apples : List<Apple>
    

    好吧,它不能实现你100%的愿望(因为它似乎不可能实现),但它应该做到这一点

    public interface IFruits
    {
        Fruit GetItem();
        void ProcessItem(Fruit fruit);
        void Check();
    }
    
    // I changed the name of your IFruitsBase, because it's the same thing as IFruits
    // No need to have 2 differents names to name the same thing    
    public interface IFruits<T> : IFruits where T : Fruit
    {
        T GetItem();
        void ProcessItem(T fruit);
        void Check(); // This one could probably be removed from this interface
    }
    

    好吧,它不能实现你100%的愿望(因为它似乎不可能实现),但它应该做到这一点

    public interface IFruits
    {
        Fruit GetItem();
        void ProcessItem(Fruit fruit);
        void Check();
    }
    
    // I changed the name of your IFruitsBase, because it's the same thing as IFruits
    // No need to have 2 differents names to name the same thing    
    public interface IFruits<T> : IFruits where T : Fruit
    {
        T GetItem();
        void ProcessItem(T fruit);
        void Check(); // This one could probably be removed from this interface
    }
    

    为了实现这一点,您需要构造一个协变接口。你有没有听说过,特别是?您需要将
    IFruitsBase
    重新定义为协变。即,在泛型参数上使用
    out
    关键字,并将其约束为
    IFruit
    ,如
    IFruitsBase,其中T:IFruit

    首先为
    Fruit
    Apple
    类定义接口,并在实际类中实现它们:

    public interface IFruit
    {
    }
    
    public interface IApple : IFruit
    {
    }
    
    public class Fruit : IFruit
    {
    }
    
    public class Fruit : IFruit
    {
    
    }
    
    public class Apple : Fruit, IApple
    {
    
    }
    
    然后用复数(我希望这只是一个例子,对吗?)。IMHO您可以去掉非通用接口
    IFruits
    ,并将通用接口
    IFruitsBase
    重命名为
    IFruits
    。作为编码标准,可以将基后缀应用于要继承的类

    public interface IFruits<out T> where T : IFruit
    {
        T GetItem();
        void ProcessItem(IFruit fruit);
        void Check();
    
    }
    
    public interface IApples : IFruits<IApple>
    {
        void MakeAppleJuice(IEnumerable<IApple> apples);
    }
    
    public class Fruits<T> : IFruits<T> where T : IFruit
    {
        public void Check()
        {
            throw new NotImplementedException();
        }
    
        public T GetItem()
        {
            throw new NotImplementedException();
        }
    
        public void ProcessItem(IFruit fruit)
        {
            if (fruit is T)
            {
    
            }
            else
            {
                throw new NotSupportedException();
            }
        }
    }
    
    public class Apples : Fruits<IApple>, IApples
    {
        public void MakeAppleJuice(IEnumerable<IApple> apples)
        {
            // Do the juice
        }
    }
    

    现在,只有当您访问
    context.Apples
    时,
    makeappleguice()
    方法才可用

    您需要构造一个协变接口来实现这一点。你有没有听说过,特别是?您需要将
    IFruitsBase
    重新定义为协变。即,在泛型参数上使用
    out
    关键字,并将其约束为
    IFruit
    ,如
    IFruitsBase,其中T:IFruit

    首先为
    Fruit
    Apple
    类定义接口,并在实际类中实现它们:

    public interface IFruit
    {
    }
    
    public interface IApple : IFruit
    {
    }
    
    public class Fruit : IFruit
    {
    }
    
    public class Fruit : IFruit
    {
    
    }
    
    public class Apple : Fruit, IApple
    {
    
    }
    
    然后用复数(我希望这只是一个例子,对吗?)。IMHO您可以去掉非通用接口
    IFruits
    ,并将通用接口
    IFruitsBase
    重命名为
    IFruits
    。作为编码标准,可以将基后缀应用于要继承的类

    public interface IFruits<out T> where T : IFruit
    {
        T GetItem();
        void ProcessItem(IFruit fruit);
        void Check();
    
    }
    
    public interface IApples : IFruits<IApple>
    {
        void MakeAppleJuice(IEnumerable<IApple> apples);
    }
    
    public class Fruits<T> : IFruits<T> where T : IFruit
    {
        public void Check()
        {
            throw new NotImplementedException();
        }
    
        public T GetItem()
        {
            throw new NotImplementedException();
        }
    
        public void ProcessItem(IFruit fruit)
        {
            if (fruit is T)
            {
    
            }
            else
            {
                throw new NotSupportedException();
            }
        }
    }
    
    public class Apples : Fruits<IApple>, IApples
    {
        public void MakeAppleJuice(IEnumerable<IApple> apples)
        {
            // Do the juice
        }
    }
    

    现在,只有当您访问
    context.Apples
    时,
    makeappleguice()
    方法才可用

    我希望我没有看错这个问题,这也不是太愚蠢:)

    公共接口IFruit
    {
    }
    公共接口应用:IFruit
    {
    }
    公共接口IFruit,其中T:IFruit
    {
    T GetItem();
    无效处理项目(T水果);
    无效检查();
    }
    公共类水果:IFruit其中T:IFruit
    {
    公共作废检查()
    {
    抛出新的NotImplementedException();
    }
    公共T GetItem()
    {
    抛出新的NotImplementedException();
    }
    公共物品(T水果)
    {
    抛出新的NotImplementedException();
    }
    }
    公共接口应用程序
    {
    void MakeAppleJuice(可数苹果);
    }
    公共类苹果:水果、苹果
    {
    public void MakeAppleJuice(IEnumerable apples)
    {
    抛出新的NotImplementedException();
    }
    }
    公共类上下文
    {
    公共IApples苹果{get;set;}
    公共上下文()
    {
    这个苹果=新苹果();
    }
    公共图书馆
    
    public interface IApples : IFruits<Apple>
    
    public class Fruits<T> : IFruits<T> where T : Fruit
    
    var context = new Context();
    context.Apples.[You will have access to both IFruits and IFruits<Apple>]
    
    var context = new Context();
    context.GetFruits(item.GetType()).[only IFruits methods are accessable]
    
    public interface IFruit
    {
    }
    
    public interface IApple : IFruit
    {
    }
    
    public class Fruit : IFruit
    {
    }
    
    public class Fruit : IFruit
    {
    
    }
    
    public class Apple : Fruit, IApple
    {
    
    }
    
    public interface IFruits<out T> where T : IFruit
    {
        T GetItem();
        void ProcessItem(IFruit fruit);
        void Check();
    
    }
    
    public interface IApples : IFruits<IApple>
    {
        void MakeAppleJuice(IEnumerable<IApple> apples);
    }
    
    public class Fruits<T> : IFruits<T> where T : IFruit
    {
        public void Check()
        {
            throw new NotImplementedException();
        }
    
        public T GetItem()
        {
            throw new NotImplementedException();
        }
    
        public void ProcessItem(IFruit fruit)
        {
            if (fruit is T)
            {
    
            }
            else
            {
                throw new NotSupportedException();
            }
        }
    }
    
    public class Apples : Fruits<IApple>, IApples
    {
        public void MakeAppleJuice(IEnumerable<IApple> apples)
        {
            // Do the juice
        }
    }
    
    public class Context
    {
        public IApples Apples { get; set; }
    
        public IBananas Bananas { get; set; }
    
        public Context()
        {
            Apples = new Apples();
        }
    
        public IFruits<T> GetFruits<T>() where T : IFruit
        {
            return new Fruits<T>();
        }
    }
    
    public interface IFruit
    {
    }
    public interface IApple: IFruit
    {
    }
    public interface IFruits<T> where T : IFruit
    {
        T GetItem();
        void ProcessItem(T fruit);
        void Check();
    }
    public class Fruits<T> : IFruits<T> where T : IFruit
    {
        public void Check()
        {
            throw new NotImplementedException();
        }
    
        public T GetItem()
        {
            throw new NotImplementedException();
        }
    
        public void ProcessItem(T fruit)
        {
            throw new NotImplementedException();
        }
    }
    public interface IApples 
    {
        void MakeAppleJuice(IEnumerable<IApple> apples);
    }
    public class Apples : Fruits<IApple>, IApples
    {
        public void MakeAppleJuice(IEnumerable<IApple> apples)
        {
            throw new NotImplementedException();
        }
    }
    public class Context
    {
        public IApples Apples { get; set; }
    
        public Context()
        {
            this.Apples = new Apples();
        }
    
        public IFruits<IFruit> GetFruits<T>()
        {
            return null;
        }
        private void test()
        {
            //this.GetFruits<IFruit>().ProcessItem(
            //this.GetFruits<IApple>().ProcessItem(
            //this.Apples.MakeAppleJuice(            
        }
    }
    
    public class Foo
    {
        public void Main()
        {
            var context = new Context();
            Check(new IFruits<IFruit>[] { context.GetFruits<IApple>() });
        }
        public void Check(IEnumerable<IFruits<IFruit>> fruits) 
        {
            foreach (var fruit in fruits)
                fruit.Check();
        }
    }