繁体   English   中英

是否有内置的通用接口,索引器返回了协变类型参数?

[英]Is there a built-in generic interface with covariant type parameter returned by an indexer?

在这个帖子中

如何获取null而不是按键访问字典值的KeyNotFoundException?

在我自己的回答中,我使用显式接口实现来更改基本字典索引器行为,如果KeyNotFoundException中没有键,则不会抛出KeyNotFoundException (因为在这种情况下,我很方便KeyNotFoundException联中获取null )。

这里是:

public interface INullValueDictionary<T, U>
    where U : class
{
    U this[T key] { get; }
}

public class NullValueDictionary<T, U> : Dictionary<T, U>, INullValueDictionary<T, U>
    where U : class
{
    U INullValueDictionary<T, U>.this[T key]
    {
        get
        {
            if (ContainsKey(key))
                return this[key];
            else
                return null;
        }
    }
}

因为在一个真实的应用程序中我有一个字典列表,我需要一种方法来从集合中访问字典作为接口。 我使用简单的int索引器来访问列表的每个元素。

var list = new List<NullValueDictionary<string, string>>();
int index = 0;
//...
list[index]["somekey"] = "somevalue";

最简单的事情是做这样的事情:

var idict = (INullValueDictionary<string, string>)list[index];
string value = idict["somekey"];

当我决定尝试使用协方差特征来代替使用接口集合时提出的问题。 所以我需要一个带有协变类型参数的接口才能使转换工作。 我想到的第一件事是IEnumerable<T> ,所以代码看起来像这样:

IEnumerable<INullValueDictionary<string, string>> ilist = list;
string value = ilist.ElementAt(index)["somekey"];

不是那么好,除了ElementAt而不是索引器更糟糕。 List<T>的索引器在IList<T>定义,而T没有协变。

我该怎么办? 我决定写自己的:

public interface IIndexedEnumerable<out T>
{
    T this[int index] { get; }
}

public class ExtendedList<T> : List<T>, IIndexedEnumerable<T>
{

}

好吧,几行代码(我甚至不需要在ExtendedList<T>写任何东西),它可以按我的意愿工作:

var elist = new ExtendedList<NullValueDictionary<string, string>>();
IIndexedEnumerable<INullValueDictionary<string, string>> ielist = elist;
int index = 0;
//...
elist[index]["somekey"] = "somevalue";
string value = ielist[index]["somekey"];

最后一个问题是:这个协变演员能否以某种方式实现,而无需创建额外的收藏?

您可以尝试使用由List<T>实现的IReadOnlyList<T> List<T>

请注意 ,我已将一个NullValueDictionary<string, string>实例添加到List ,这样您就不会在elist[index]行中获得ArgumentOutOfRangeException

IReadOnlyList<NullValueDictionary<string, string>> elist = new List<NullValueDictionary<string, string>>
                                                                    { 
                                                                        new NullValueDictionary<string, string>() 
                                                                    };
IReadOnlyList<INullValueDictionary<string, string>> ielist = elist;

int index = 0;
//...
elist[index]["somekey"] = "somevalue";
string value = elist[index]["somekey"];

编辑:我已经在.NET 4.5之前搜索了带有索引的协变接口和集合,但没有找到。 我认为还有一个更简单的解决方案,而不是创建单独的界面 - 只是为了将一个集合转换为另一个集合。

List<INullValueDictionary<string, string>> ielist = elist.Cast<INullValueDictionary<string, string>>().ToList();

或者使用从数组中获得的协方差

INullValueDictionary<string, string>[] ielist = elist.ToArray()

LINQ有一些优化可以处理整个类型的兼容性,因此如果这些类型兼容,您将不会迭代序列。

演员实施取自MONO Linq

public static IEnumerable<TResult> Cast<TResult> (this IEnumerable source)
{
    var actual = source as IEnumerable<TResult>;
    if (actual != null)
        return actual;

    return CreateCastIterator<TResult> (source);
}

请注意,我已将INullValueDictionary<T, U>接口更改为包含属性中的set ,以便ielist[index]["somekey"] = "somevalue"; 将工作。

public interface INullValueDictionary<T, U> where U : class
{
    U this[T key] { get; set; }
}

但是再次 - 如果创建一个新的接口和类对你来说没问题而且你不想在任何地方乱扔乱码 - 我认为这是一个很好的解决方案,如果你考虑过限制,它会给出。

寻找mscorlib中的协方差

这对您来说可能不会感兴趣,但我只是想知道mscorlib程序集中哪些类型是协变的。 通过运行下一个脚本我收到的只有17种类型是协变的,其中9种是Func 我省略了IsCovariant实现,因为即使没有它,这个答案太长了

typeof(int).Assembly.GetTypes()
                    .Where(type => type.IsGenericType)
                    .Where(type=>type.GetGenericArguments().Any(IsCovariant))
                    .Select(type => type.Name)
                    .Dump();

//Converter`2 
//IEnumerator`1 
//IEnumerable`1 
//IReadOnlyCollection`1 
//IReadOnlyList`1 
//IObservable`1 
//Indexer_Get_Delegate`1 
//GetEnumerator_Delegate`1 

暂无
暂无

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

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