C# 空合并运算符IList,数组,可枚举。foreach中为空
在这方面,我发现:C# 空合并运算符IList,数组,可枚举。foreach中为空,c#,.net,collections,null-coalescing-operator,C#,.net,Collections,Null Coalescing Operator,在这方面,我发现: int[] array = null; foreach (int i in array ?? Enumerable.Empty<int>()) { System.Console.WriteLine(string.Format("{0}", i)); } 及 在一个NotifyCollectionChangedEventHandler中,我想应用可枚举.Empty如下: foreach (DrawingPoint drawingPoint
int[] array = null;
foreach (int i in array ?? Enumerable.Empty<int>())
{
System.Console.WriteLine(string.Format("{0}", i));
}
及
在一个NotifyCollectionChangedEventHandler
中,我想应用可枚举.Empty
如下:
foreach (DrawingPoint drawingPoint in e.OldItems ?? Enumerable.Empty<DrawingPoint>())
this.RemovePointMarker(drawingPoint);
及
很好用
为什么会这样?为什么
IList??T[]
工作,但IList??IEnumerable
不是吗?我认为它决定了第一个成员的结果类型,在您的例子中,它是IList。第一种情况有效,因为数组实现了IList。对于IEnumerable,这不是真的
这只是我的猜测,因为没有细节
UPD。正如在接受的问题中所指出的,在使用此表达式时,C#规范(or)中有更多关于此主题的详细信息:
a ?? b
然后b
必须是与a
相同的类型,或者它必须隐式地可转换为该类型,这意味着它必须实现或继承a
所属的任何类型
这些工作:
SomethingThatIsIListOfT ?? new T[0]
SomethingThatIsIListOfT ?? new T[] { }
因为T[]
是IList
,所以数组类型实现了该接口
但是,这是行不通的:
SomethingThatIsIListOfT ?? SomethingThatImplementsIEnumerableOfT
因为表达式的类型将是a
类型,并且编译器显然无法保证somethingtheimplementsienuerableft
也实现IList
您必须从两个方面中选择一个,这样您就有了兼容的类型:
(IEnumerable<T>)SomethingThatIsIListOfT ?? SomethingThatImplementsIEnumerableOfT
(IEnumerable)某个东西是什么??有些东西是可以计算的
现在表达式的类型是IEnumerable
,而??
操作符可以完成它的工作
“表达式类型将为
a
”稍微简化,规范全文如下:
表达式的类型
a??b
取决于操作数上可用的隐式转换。按照优先顺序,a??b
是A0
,A
或b
,其中A
是A的类型(前提是A
有一个类型),b
是b
的类型(前提是b
有一个类型),如果A
是可空的类型,A0
是的基本类型,或A
否则。具体来说,a??b
处理如下:
- 如果
A
存在并且不是可空类型或引用类型,则会发生编译时错误
- 如果
b
是动态表达式,则结果类型是动态的。在运行时,首先计算a
。如果a
不是null
,a
将转换为动态类型,这将成为结果。否则,b
将被评估,结果将成为结果
- 否则,如果
A
存在并且是可空类型,并且存在从b
到A0
的隐式转换,则结果类型为A0
。在运行时,首先计算a
。如果a
不是null
,a
被展开为类型A0
,并成为结果。否则,b
将被计算并转换为类型A0
,并成为结果李>
- 否则,如果存在
A
,并且存在从b
到A
的隐式转换,则结果类型为A
。在运行时,首先计算a
。如果a
不为空,a
将成为结果。否则,b
将被计算并转换为类型A
,并成为结果
- 否则,如果
b
具有类型b
,并且存在从a
到b
的隐式转换,则结果类型为b
。在运行时,首先计算a
。如果a
不为null
,a
被展开为类型A0
(如果a
存在且可为null),并转换为类型B
,并成为结果。否则,b
将被计算并成为结果
- 否则,
a
和b
不兼容,会发生编译时错误
您正在使用非泛型System.Collections.IList
和泛型System.Collections.generic.IEnumerable
作为操作符的操作数。因为两个接口都不继承另一个,所以这将不起作用
我建议你:
foreach (DrawingPoint drawingPoint in e.OldItems ?? Array.Empty<DrawingPoint>())
...
或者只是:
public static class EmptyArray<TElement>
{
public static readonly TElement[] Value = { };
}
请注意?。
的使用,以及我们现在可以使用var
的事实。T[]
是一个IList
,但是不能保证实现IEnumerable
的对象实现IList
。您必须将IList
对象强制转换为IEnumerable
,才能将其与一起使用??可枚举。为空
。谢谢。在中,您可以看到它实现了System.Collections.IList
。对于IList
,是否有等价的Enumerable.Empty
?除了new T[0]
,我不知道还有什么,当然可能有一些我不知道的东西。要么new T[]
要么new List()
就可以了,as@LasseVågsætherKarlsensaid@IDarkCoder与newt[0]
相比,有一种方法更可取,因为后者总是构造一个新的数组实例,而array.Empty会重用相同的实例。这正是我要寻找的!但是我只限于.Net 4.5.2,所以我还不能使用它,因为它的4.6+:(C#规范有明确的细节,我已经在我自己的答案中包含了它。我想创建一个类似于int[0]的单例将一直工作到我们获得.NET4.6…关于IList
,我不能-不在它上面投入太多的工作,这在这里是不值得的-改变tha的类型
SomethingThatIsIListOfT ?? SomethingThatImplementsIEnumerableOfT
(IEnumerable<T>)SomethingThatIsIListOfT ?? SomethingThatImplementsIEnumerableOfT
foreach (DrawingPoint drawingPoint in e.OldItems ?? Array.Empty<DrawingPoint>())
...
public static class EmptyArray<TElement>
{
public static readonly TElement[] Value = new TElement[] { };
}
public static class EmptyArray<TElement>
{
public static readonly TElement[] Value = { };
}
foreach (DrawingPoint drawingPoint in e.OldItems ?? EmptyArray<DrawingPoint>.Value)
...
foreach (var drawingPoint in
e.OldItems?.Cast<DrawingPoint> ?? Enumerable.Empty<DrawingPoint>()
)
...