简体   繁体   English

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

[英]Generic and non-generic interfaces and inheritance - ambiguity while accessing

I have the following setup 我有以下设置

  • Base class 基类

     public class Fruit { } 
  • Inherited class 继承的类

     public class Apple : Fruit { } 
  • Generic Base interface 通用基础接口

     public interface IFruitsBase<T> where T : Fruit { T GetItem(); void ProcessItem(T fruit); void Check(); } 
  • Non generic Interface that inheritates from generic interface with the base class as the generic 从通用接口继承的非通用接口,基类为通用

     public interface IFruits : IFruitsBase<Fruit> { } 
  • Specific interface of the correspoing class 对应类的特定接口

     public interface IApples : IFruitsBase<Apple>, IFruits { void MakeAppleJuice(IEnumerable<Apple> apples); } 
  • Base implementation class 基本实现类

     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); } } 
  • Specific implementation 具体实施

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

The problem I am facing, is at that part: 我所面临的问题就在那部分:

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

Here I am getting the message: 在这里,我得到消息:

Possible ambiguity while accessing by this Interface Apple IFruitsBase<Apple> Get() Fruit IFruitsBase<Fruit>.Get() 通过此接口访问时可能存在歧义Apple IFruitsBase<Apple> Get() Fruit IFruitsBase<Fruit>.Get()

To fix the problem I could remove the interface IFruits from IApples , but this gives other errors as in: 要解决这个问题,我可以删除接口IFruitsIApples ,但是这给其他错误,如:

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

My goal is if someone accesses the objects with: 我的目标是是否有人通过以下方式访问对象:

var context = new Context();
context.Apples.[only apple relevant methods should be accessable]

if someone access it with: 如果有人通过以下方式访问它:

var context = new Context();
context.GetFruits(item.GetType()).[only fruit relevant methods should be accessable]

and no matter if one got it with method a or b, all should be passable to methods with a signature of 并且无论是使用方法a还是b获得的,都应该可以传递给具有签名的方法

void Foo(IFruits fruits);

Well, it doesn't do 100% of your wish (because it seems pretty impossible to accomplish), but it should do the trick. 好吧,它并不能完全满足您的期望(因为似乎很难实现),但是应该可以解决。

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
}

Then: 然后:

public interface IApples : IFruits<Apple>

And: 和:

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

Now, IFruits inherits from IFruits, so no more conflicts, because you won't implement twice the same interface. 现在,IFruits继承自IFruits,因此不再有冲突,因为您将不会实现两次相同的接口。


So, if you do: 因此,如果您这样做:

var context = new Context();
context.Apples.[You will have access to both IFruits and IFruits<Apple>]

And with this: 并与此:

var context = new Context();
context.GetFruits(item.GetType()).[only IFruits methods are accessable]

You need to construct a covariant interface in order to accomplish this. 您需要构造一个协变接口才能完成此任务。 Have you heard of the variance in C# and variant interfaces in particular? 您是否听说过C#变量接口差异 You need to re-define IFruitsBase<T> to be covariant. 您需要重新定义IFruitsBase<T>以进行协变。 Ie, use the out keyword on the generic parameter and constraint it to be an IFruit , like this IFruitsBase<out T> where T : IFruit 即,在通用参数上使用out关键字并将其约束为IFruit ,例如IFruitsBase<out T> where T : IFruit

Start off by defining interfaces for the Fruit and Apple classes and implement them in your actual classes: 首先为FruitApple类定义接口,然后在实际的类中实现它们:

public interface IFruit
{
}

public interface IApple : IFruit
{
}

public class Fruit : IFruit
{
}

public class Fruit : IFruit
{

}

public class Apple : Fruit, IApple
{

}

Then go with the plurals (I hope this is just for the example, right?). 然后使用复数(我希望这仅是示例,对吧?)。 IMHO you can get rid of the non-generic interface IFruits and rename the generic IFruitsBase<T> as IFruits<T> . 恕我直言,您可以摆脱非通用接口IFruits并将通用IFruitsBase<T>重命名为IFruits<T> As a coding standard, you apply the Base suffix to classes meant to be inherited. 作为编码标准,可以将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
    }
}

Then modify the Context class a bit: 然后修改一下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>();
    }
}

Now the MakeAppleJuice() method is available ONLY when you access context.Apples . 现在,仅当您访问context.Apples时, MakeAppleJuice()方法才可用。

I hope I did not understand the question wrong, and this is not too stupid :) 我希望我不明白这个问题错了,这也不是太愚蠢:)

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

In my opinion, your public interface IFruits : IFruitsBase<Fruit> is wrong. 我认为您的public interface IFruits : IFruitsBase<Fruit>是错误的。 You don't declare a non-generic interface based on a generic one, you do it the other way around. 您不会基于泛型接口声明非泛型接口,而是以另一种方式进行声明。 That way it makes more sense. 这样更有意义。

This is what I have come up with: 这是我想出的:

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

I think if you unpack what you're trying to do here, a solution presents itself. 我认为,如果您解压缩要在此处执行的操作,则会找到解决方案。

First, I dislike using plural classes to represent a list. 首先,我不喜欢使用复数类来表示列表。 ie

Apple
Apples : List<Apple>

That's not exactly what you were doing, but its basically the same thing. 那不完全是你在做什么,但是基本上是一样的。 IMO doing this creates harder to maintain code and can obscure what you're trying to do. IMO这样做会使维护代码变得更加困难,并且会使您试图做的事情变得晦涩。 Instead I've replaced it with extension methods on arrays of Apple 相反,我已将其替换为Apple数组上的扩展方法

I also pulled out everything I wasn't using, so I may have missed something, but I tried to include everything it seemed like you were asking for. 我还拉出了所有我不使用的东西,所以我可能错过了一些东西,但是我尝试包括似乎在要求的所有东西。 This is what I would go with based off what you have. 这就是我根据您所拥有的去进行的工作。

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.
    }
}

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

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