I defined the following struct:
public struct Call
{
public SourceFile caller;
public SourceFile callee;
public Call(SourceFile caller, SourceFile callee)
{
this.caller = caller;
this.callee = callee;
}
}
Later, I assign it to the Tag property of another object:
line.Tag = new Call(sf1, sf2);
But when I try to retrieve the Tag property like so,
Call call = line.Tag as Call;
Visual Studio gives the following compile-time error:
The operator as must be used within a
reference type or nullable type
What is the meaning of that? And how can I solve it?
A struct is a value type, so it cannot be used with the as
operator. The as
operator must be able to assign a value of null if the cast fails. This is only possible with a reference type or a nullable value type.
There are a couple ways to solve this, but your best bet is to change your Call
type from a struct to a class. This will essentially change your type from a value type to a reference type, which allows the as
operator to assign a value of null if the cast fails.
For more information on value types vs. reference types, this is a decent article. Also, have a look on MSDN:
- value types
- reference types
- as-operator
- nullable types.
Some of the existing answers aren't quite right. You can't use non-nullable types with as
, because the result of as
is the null value of the type if the first operand isn't actually of an appropriate type.
However, you can use as
with value types... if they're nullable:
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
So you could use:
Call? call = line.Tag as Call?;
Then you can use it as:
if (call != null)
{
// Do stuff with call.Value
}
Two caveats though:
- In my experience this is slower than just using
is
followed by a cast
- You should seriously reconsider your current
Call
type:
- It's exposing public fields, which is generally poor encapsulation
- It's a mutable value type, which is almost certainly a mistake
I would strongly suggest you make it a class instead - at which point this problem goes away anyway.
Another thought: if the tag should always be a Call
, then it's better to cast it:
Call call = (Call) line.Tag;
That way, if the data doesn't match your expectation (i.e. there's some bug such that the Tag
isn't a Call
) then you get to find out about it early, rather than after you've potentially done some other work. Note that this cast will behave differently depending on whether Call
is a struct or a class, if Tag
is null - you can cast a null value to a variable of a reference type (or a nullable value type), but not to a non-nullable value type.
From the C# Spec
§7.10.11 The as operator is used to
explicitly convert a value to a given
reference type or nullable type. Unlike a cast expression
(§7.7.6), the as operator never throws
an exception. Instead, if the
indicated conversion is not possible,
the resulting value is null.
References and nullable types can be null. Stucts are value types so they can't be null.
Call? call = line.Tag as Call?;
It's a limitation of C#. If the type were a reference type, then if the cast failed it would simply return 'null', but since it's a value type, it doesn't know what to return when the cast fails.
You must replace your use of as with two: 'is' and 'as'
if (line.Tag is Call) {
call = (Call)line.Tag;
} else {
// Do whatever you would do if as returned null.
}
What is the meaning - As stated, structures are value types.
How can I solve it - Change it to
Call call = line.Tag;