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>()
  )
  ...