通用和非通用接口和继承 - 访问时的模糊性

时间:2017-12-01 09:35:27

标签: 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();
    }
    
  • 非泛型接口,它继承自基类的泛型接口作为泛型

    public interface IFruits : IFruitsBase<Fruit>
    {
    
    }
    
  • 相应类的具体接口

    public interface IApples : IFruitsBase<Apple>, IFruits
    {
        void MakeAppleJuice(IEnumerable<Apple> apples);
    }
    
  • 基础实施类

    public class Fruits<T> : IFruitsBase<T>, IFruits where T : Fruit
    {
        public T GetItem()
        {
            return null;
        }
        public void ProcessItem(T fruit)
        {
    
        }
        public void Check()
        {
    
        }
    
        Fruit IFruitsBase<Fruit>.GetItem()
        {
            return this.GetItem();
        }
        void IFruitsBase<Fruit>.ProcessItem(Fruit fruit)
        {
            ProcessItem((T)fruit);
        }
    }
    
  • 具体实施

    public class Apples : Fruits<Apple>, IApples
    {
        public void MakeAppleJuice(IEnumerable<Apple> apples)
        {
    
        }
    }
    

我面临的问题是:

public interface IApples : IFruitsBase<Apple>, IFruits
{
    void MakeAppleJuice(IEnumerable<Apple> apples);
}

我收到的消息是:

  

通过此界面Apple IFruitsBase<Apple> Get() Fruit IFruitsBase<Fruit>.Get()

访问时可能存在歧义

要解决此问题,我可以从IFruits删除界面IApples,但这会产生其他错误,如下所示:

public class Context
{
    public IApples Apples { get; set; }

    public Context()
    {
        this.Apples = new Apples();
    }

    public IFruits GetFruits(Type type)
    {
        return this.Apples; //simplified the code here, it should actually get the member of this that fits the type
    }
}

public class Foo
{
    public void Main()
    {
        var context = new Context();
        Check(new IFruits[] { context.Apples }); //can't do that since context.Apples doesnt inherit from IFruits, to fix the above ambiguity
    }

    public void Check(IEnumerable<IFruits> fruits)
    {
        foreach (var fruit in fruits)
            fruit.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);

5 个答案:

答案 0 :(得分:1)

嗯,它没有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
}

然后:

public interface IApples : IFruits<Apple>

public class Fruits<T> : IFruits<T> where T : Fruit

现在,IFruits继承自IFruits,所以没有更多的冲突,因为你不会实现两次相同的接口。

所以,如果你这样做:

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]

答案 1 :(得分:1)

您需要构建一个协变接口才能实现此目的。您是否听说过variance in C#variant interfaces?您需要将IFruitsBase<T>重新定义为协变。即,在泛型参数上使用out关键字并将其约束为IFruit,如此IFruitsBase<out T> where T : IFruit

首先定义FruitApple类的接口,然后在实际的类中实现它们:

public interface IFruit
{
}

public interface IApple : IFruit
{
}

public class Fruit : IFruit
{
}

public class Fruit : IFruit
{

}

public class Apple : Fruit, IApple
{

}

然后使用复数形式(我希望这仅仅是为了示例,对吧?)。恕我直言,您可以摆脱非通用接口IFruits并将通用IFruitsBase<T>重命名为IFruits<T>。作为编码标准,您将 Base 后缀应用于要继承的类。

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类:

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>();
    }
}

现在MakeAppleJuice()方法仅在您访问context.Apples时可用。

答案 2 :(得分:1)

我希望我不明白这个问题是错的,这不是太愚蠢:)

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();
    }
}

答案 3 :(得分:0)

在我看来,你的public interface IFruits : IFruitsBase<Fruit>是错误的。您没有基于通用接口声明非泛型接口,而是以相反的方式执行。这样更有意义。

这就是我的想法:

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);
}

答案 4 :(得分:0)

我认为如果你解开你在这里尝试做的事情,就会出现一个解决方案。

首先,我不喜欢使用复数类来表示列表。即

Apple
Apples : List<Apple>

这不完全是你在做什么,但它基本上是一回事。 IMO这样做会增加维护代码的难度,并且会掩盖您尝试做的事情。相反,我已使用Apple

数组上的扩展方法替换它

我也拿出了我没有使用的所有东西,所以我可能错过了一些东西,但我试图把你想要的一切都包括在内。这就是我根据你所拥有的东西。

public class Context
{
    public Apple[] Apples { get; set; }

    public IFruit[] GetFruits()
    {
        return null;
    }
}

public interface IFruit
{

}

public class Apple : IFruit
{

}

public class Banana : IFruit
{

}

public static class Apples
{
    public static void MakeAppleJuice(this Apple[] apples)
    {

    }

    public static void ProcessItem(this Apple[] apples, Apple fruit)
    {
    }
}



class Program
{
    static void Foo(IFruit[] fruits)
    {

    }

    static void Main(string[] args)
    {
        var context = new Context();
        context.Apples.MakeAppleJuice();
        context.GetFruits();
        Foo(context.Apples); //Dangerous call, but legal.
        Foo(context.GetFruits());
        context.Apples.ProcessItem(new Banana()); // Does not compile.
    }
}