简体   繁体   English

C#接口列表的继承

[英]C# Inheritance of lists of interfaces

I'm a bit new to C# interfaces, and have run into a rather awkward solution to an extension method on inherited interface lists problem. 我对C#接口有点新,并且在继承的接口列表问题上遇到了一个相当尴尬的扩展方法解决方案。 Example interfaces look like this: 示例接口如下所示:

public interface IData
{
    int Value { get; set; }
}

public interface IDataWithName : IData
{
    string Name { get; set; }
}

public interface IDataContainer
{
    IList<IData> DataList { get; set; }
}

public interface IDataWithNameContainer : IDataContainer
{
    new IList<IDataWithName> DataList { get; set; }
}

With an extension method: 使用扩展方法:

public static class ExtensionMethod
{
    public static int CountNumberOfIDataItems(this IDataContainer i)
    {
        return i.DataList.Count();
    }
}

When implementing these interfaces, both the IList DataList and IList IDataContainer.DataList need to be implemented. 实现这些接口时,需要实现IList DataList和IList IDataContainer.DataList。 While this is possible, the resulting code is inelegant: 虽然这是可能的,但结果代码是不优雅的:

public class DataNameImplimentatioFixed : IDataWithNameContainer
{
    public IList<IDataWithName> DataList { get; set; }
    IList<IData> IDataContainer.DataList
    {
        get => new List<IData>(DataList);
        set
        {
            DataList = new List<IDataWithName>();
            foreach (IDataWithName _dataLoop in value) { DataList.Add(_dataLoop); }
        }
    }
}

Duplicating the list each time that the extension is called could lead to some performance problems, as well as needing an additional check that any further methods on IDataContainer won't try to add classes that inherit from IData but not IDataWithName. 每次调用扩展名时复制列表都可能导致一些性能问题,并且需要额外检查IDataContainer上的任何其他方法是否不会尝试添加从IData继承而不是IDataWithName的类。

It feels like there should be a better way of using the extension methods, that isn't as vulnerable to problems with future extensions. 感觉应该有一种更好的方法来使用扩展方法,这种方法不容易受到未来扩展问题的影响。 The best solution I can some up with is to take the IList DataList out of IDataContainer, and have a separate extension method for each class that contains a list. 我可以解决的最好的解决方案是从IDataContainer中取出IList DataList,并为包含列表的每个类提供单独的扩展方法。

Can anyone come up with a better solution than this? 任何人都可以提出比这更好的解决方案吗?

The problem is that IDataWithNameContainer hides the DataList property it inherits from IDataContainer , not the extension method. 问题是IDataWithNameContainer隐藏了它从IDataContainer继承的DataList属性,而不是扩展方法。 It shouldn't do so. 它不应该这样做。

The easy way to create a derived container class that only accepts a specific IData implementation is to make the container interfaces generic and use a stricter type constraint in IDataWithNameContainer : 创建仅接受特定IData实现的派生容器类的简单方法是使容器接口通用,并在IDataWithNameContainer使用更严格的类型约束:

public interface IData
{
    int Value { get; set; }
}

public interface IDataWithName : IData
{
    string Name { get; set; }
}

public interface IDataContainer<T> where T:IData
{
    IList<T> DataList { get; set; }
}

public interface IDataWithNameContainer<T> : IDataContainer<T> where T:IDataWithName
{

}

public static class ExtensionMethod
{
    public static int CountNumberOfIDataItems<T>(this IDataContainer<T> i) where T:IData
    {
        return i.DataList.Count;
    }
}

Creating a named container this way is easy : 以这种方式创建命名容器很容易:

public class Boo:IDataWithName 
{
    public int Value { get; set; }
    public string Name { get; set; }
}


public class BooContainer: IDataWithNameContainer<Boo>
{
    public IList<Boo> DataList { get; set; }
}

Update 更新

There's no reason to add new items to the DataList in a loop by the way. 顺便说一下,没有理由在循环中向DataList添加新项。 The List constructor can accept an IEnumerable with the initial values: List构造函数可以接受具有初始值的IEnumerable:

public class BooContainer: IDataWithNameContainer<Boo>
{
    IList<Boo> _list=new List<Boo>();
    public IList<Boo> DataList 
    { 
        get => _list; 
        set => _list=new List<Boo>(value); 
    }
}

This allows storing both lists and arrays to DataList by copying their contents into a new List. 这允许通过将列表和数组的内容复制到新的List中来将它们存储到DataList

This will throw if someone sets a null. 如果有人设置null,这将抛出。 To avoid this, one can use the null replacement operator in the setter : 为避免这种情况,可以在setter中使用null替换运算符:

    set => _list=new List<Boo>(value ?? new Boo[0]); 

You don't need IDataContainer and IDataWithNameContainer . 您不需要IDataContainerIDataWithNameContainer You already have the values in the IDataWithName interface so why is IDataWithNameContainer also inheriting from IDataContainer ? 您已经拥有IDataWithName接口中的值,那么为什么IDataWithNameContainer也继承自IDataContainer Isn't that why you made the extra interface? 这不是你制作额外界面的原因吗?

IData
IDataWithName : IData
IList<IDataWithName>  //This already has a list of (name) and (value)

Simply make your class like so: 简单地让你的课程如下:

public interface IDataWithNameContainer // Do not add this here -> : IDataContainer
{
    IList<IDataWithName> DataList { get; set; }
}

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

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