gpt4 book ai didi

c# - 是否有索引器返回的具有协变类型参数的内置通用接口(interface)?

转载 作者:太空狗 更新时间:2023-10-29 20:21:03 27 4
gpt4 key购买 nike

在这个话题中

How to get null instead of the KeyNotFoundException accessing Dictionary value by key?

在我自己的回答中,我使用显式接口(interface)实现来更改基本字典索引器行为,使其不抛出 KeyNotFoundException如果 key 不在字典中(因为在这种情况下直接内联获取 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;
}
}
}

因为在实际应用程序中我有一个字典列表,所以我需要一种方法来访问集合中的字典作为接口(interface)。我使用了简单的 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"];

当我决定尝试使用协方差特性来代替使用一组接口(interface)时提出的问题。所以我需要一个具有协变类型参数的接口(interface)才能使转换工作。我想到的第一件事是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"];

最后的问题是:是否可以在不创建额外集合的情况下以某种方式实现这种协变转换?

最佳答案

你可以尝试使用IReadOnlyList<T> ,由 List<T> 实现.

请注意,我添加了一个 NullValueDictionary<string, string> 的实例至 List , 这样你就不会得到 ArgumentOutOfRangeExceptionelist[index]线。

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 之前的索引的协变接口(interface)和集合,但一无所获。我仍然认为有比创建单独的接口(interface)更简单的解决方案 - 只需将一个集合转换为另一个集合。

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

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

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

LINQ 有一些针对整体类型兼容性的优化,因此如果这些类型兼容,您就不会遍历序列。

Cast 实现取自 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>接口(interface)包含 set在属性中,以便ielist[index]["somekey"] = "somevalue";将工作。

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

但同样 - 如果创建一个新的接口(interface)和类对你来说没问题,并且你不想在任何地方乱加转换 - 我认为这是一个很好的解决方案,如果你已经考虑了约束,它提供了。

在 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

关于c# - 是否有索引器返回的具有协变类型参数的内置通用接口(interface)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14154229/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com