Почему я не могу использовать ключевое слово as для структуры?

Я определил следующую структуру:

public struct Call
{
    public SourceFile caller;
    public SourceFile callee;

    public Call(SourceFile caller, SourceFile callee)
    {
        this.caller = caller;
        this.callee = callee;
    }
}

Позже я назначаю его свойству Tag другого объекта:

line.Tag = new Call(sf1, sf2);

Но когда я пытаюсь получить свойство Tag таким образом,

Call call = line.Tag as Call;

Visual Studio выдает следующую ошибку времени компиляции:

Оператор as должен использоваться внутри ссылочного типа или типа, допускающего значение NULL.

Что это значит? И как я могу это решить?


person Community    schedule 09.12.2010    source источник
comment
Почему вы сделали это структурой? Изменяемые структуры — плохая идея.   -  person Mark Byers    schedule 09.12.2010
comment
@Mark: +1 И структура в любом случае будет упакована для хранения в свойстве Tag ...   -  person cdhowie    schedule 09.12.2010
comment
Хм, это не должно быть изменчивым. Но я думаю, что я должен лучше нас класс для этой цели.   -  person    schedule 09.12.2010


Ответы (5)


Структура — это тип значения, поэтому ее нельзя использовать с оператором as. Оператор as должен иметь возможность присваивать значение null, если приведение не удается. Это возможно только для ссылочного типа или типа значения, допускающего значение NULL.

Есть несколько способов решить эту проблему, но лучше всего изменить тип Call со структуры на класс. Это существенно изменит ваш тип с типа значения на ссылочный тип, что позволяет оператору as присваивать значение null, если приведение не удается.

Дополнительную информацию о типах значений и ссылочных типах можно найти в этой достойной статье. Кроме того, посмотрите на MSDN:

person Jason Down    schedule 09.12.2010
comment
Ключ не может присвоить значение null, если приведение не удалось! +1 - person James King; 09.12.2010

Некоторые из существующих ответов не совсем верны. Вы не можете использовать типы non-nullable с as, потому что результатом as является нулевое значение типа, если первый операнд на самом деле не имеет подходящего типа.

Однако вы можете использовать as с типами значений... если они допускают значение NULL:

int a = 10;
object o = a;

int? x = o as int?; // x is a Nullable<int> with value 10
long? y = o as long?; // y is a Nullable<long> with the null value

Итак, вы можете использовать:

Call? call = line.Tag as Call?;

Затем вы можете использовать его как:

if (call != null)
{
    // Do stuff with call.Value
}

Два предостережения, хотя:

  • По моему опыту, это медленнее, чем простое использование is с последующим приведением
  • You should seriously reconsider your current Call type:
    • It's exposing public fields, which is generally poor encapsulation
    • Это изменяемый тип значения, что почти наверняка является ошибкой.

Я настоятельно рекомендую вам вместо этого сделать его классом - и в этот момент эта проблема все равно исчезнет.

Еще одна мысль: если тег всегда должен быть Call, то лучше его привести:

Call call = (Call) line.Tag;

Таким образом, если данные не соответствуют вашим ожиданиям (например, есть какая-то ошибка, из-за которой Tag не является Call), вы узнаете об этом заранее, а не после того, как потенциально проделаете какую-то другую работу. Обратите внимание, что это приведение будет вести себя по-разному в зависимости от того, является ли Call структурой или классом, если Tag имеет значение null, вы можете привести нулевое значение к переменной ссылочного типа (или типу значения, допускающего значение NULL), но не к не- тип значения, допускающий значение NULL.

person Jon Skeet    schedule 09.12.2010

Из спецификации С#

§7.10.11 Оператор as используется для явного преобразования значения в заданный ссылочный тип или тип, допускающий значение NULL. В отличие от выражения приведения (§7.7.6), оператор as никогда не генерирует исключение. Вместо этого, если указанное преобразование невозможно, результирующее значение равно null.

Ссылки и типы, допускающие значение NULL, могут быть нулевыми. Stucts являются типами значений, поэтому они не могут быть нулевыми.

person Conrad Frix    schedule 09.12.2010

Это ограничение C#. Если бы тип был ссылочным, то в случае неудачного приведения он просто вернул бы 'null', но, поскольку это тип значения, он не знает, что возвращать при неудачном приведении.

Вы должны заменить использование as на два: 'is' и 'as'

if (line.Tag is Call) {
  call = (Call)line.Tag;
} else {
  // Do whatever you would do if as returned null.
}
person John Doty    schedule 09.12.2010
comment
Я бы не назвал это ограничением C# — это звучит как то, что нужно исправить. Это ограничение, естественное в зависимости от того, для чего предназначен оператор as. - person Jon Skeet; 09.12.2010
comment
Может быть. Были и другие допустимые конструктивные особенности. C# 1.0 мог бы сделать выражение «x as T» возвращающим значение по умолчанию (T), если x нельзя было явно преобразовать в T. С введением типов, допускающих значение NULL, C# мог сделать возвращаемый тип «x as T» Nullable‹T› когда T является типом значения. - person John Doty; 10.12.2010
comment
(Я также должен сказать, что, учитывая количество раз, когда новые программисты спотыкаются о типы «как по значению», ограничение, на мой взгляд, является точным описанием.) - person John Doty; 10.12.2010

В чем смысл. Как уже говорилось, структуры являются типами значений.

Как я могу это решить - изменить его на

Call call = line.Tag;
person Michael Wheeler    schedule 09.12.2010