После выпуска C # 8.0 я действительно наслаждаюсь 'безопасностью пустот' с ссылочными типами, допускающими обнуление . Однако, настраивая мою библиотеку для поддержки новой функции, я наткнулся на «проблему», на которую я действительно нигде не мог найти ответ. Я посмотрел на заметки о выпуске Microsoft и исходный код .NET, но не повезло.
TL; DR: вопрос, по сути, заключается в том, следует ли объявлять свойство IEnumerator<T>
Current
какобнуляемый ссылочный тип.
Предположим, что следующая реализация IEnumerator<T>
:
public class WebSocketClientEnumerator<TWebSocketClient> : IEnumerator<TWebSocketClient> where TWebSocketClient : WebSocketClient
{
private WebSocketRoom<TWebSocketClient> room;
private int curIndex;
private TWebSocketClient? curCli;
public WebSocketClientEnumerator(WebSocketRoom<TWebSocketClient> room)
{
this.room = room;
curIndex = -1;
curCli = default(TWebSocketClient);
}
public bool MoveNext()
{
if (++curIndex >= room.Count)
{
return false;
}
else
{
curCli = room[curIndex];
}
return true;
}
public void Reset() { curIndex = -1; }
void IDisposable.Dispose() { }
public TWebSocketClient? Current
{
get { return curCli; }
}
object IEnumerator.Current
{
get { return Current; }
}
}
И предположим, что следующий код для использования перечислителя:
public class WebSocketRoom<TWebSocketClient> : ICollection<TWebSocketClient> where TWebSocketClient : WebSocketClient
{
// ...
public void UseEnumerator()
{
var e = new WebSocketClientEnumerator<TWebSocketClient>(this);
bool hasNext = e.MoveNext();
if (hasNext)
{
WebSocketClient c = e.Current; // <= Warning on this line
}
}
// ...
}
Код,как таковой, будет генерировать предупреждение, потому что, очевидно, возвращаемый тип WebSocketClientEnumerator<TWebSocketClient>.Current
является обнуляемым ссылочным типом.
Интерфейс IEnumerator
разработан таким образом, что «следует» вызывать IEnumerator<T>.MoveNext()
метод, чтобы заранее знать, имеет ли перечислитель следующее значение, тем самым обеспечивая некоторую пустотную безопасность, но, очевидно, для компилятора это ничего не значит, вызов метода MoveNext()
не гарантирует, что перечислитель Current
свойство не равно нулю.
Я хотел бы, чтобы моя библиотека компилировалась без предупреждений, а компилятор не позволил мне оставить this.curCli
со значением null
Если в конструкторе он не объявлен как ссылочный тип, допускающий значение NULL, и если он объявлен как NULL, то «бремя» проверки нулевой ссылки передается клиенту библиотеки. Конечно, перечислители, как правило, потребляются через операторы foreach
, следовательно, они в основном управляются средой выполнения и, вероятно, не так уж и сложны. Верно, что с точки зрения семантики, имеет смысл, чтобы свойство Current
перечислителя имело значение null
, потому что не может быть данных для перечисления, но я действительно вижу конфликт между интерфейсом IEnumerator<T>
и функцией обнуляемого ссылочного типа,Мне действительно интересно, есть ли способ сделать компилятор счастливым, сохраняя при этом функциональность. А также, каковы соглашения на некоторых других языках с некоторыми механизмами безопасности void?
Я понимаю, что это своего рода открытый вопрос, но я все еще думаю, что он подходит для SO. Заранее спасибо!