Roundtrip unbounded type parameters through object - c#

An unconstrained type parameter can be a nullable reference, non-nullable reference, value type or nullable value type.
Is it possible to roundtrip a parameter through object[?] ?
The following simplified code demonstrates the problem:
public static void Foo<U>(U unconstrainedParameter)
{
object? nullableObject = unconstrainedParameter;
U assignBack = (U)nullableObject; // [CS8600] Converting null literal or possible null value to non-nullable type.
}
If we change nullableObject to type object then I get the error even at the assignment.
Is there some "trick" to do this roundtrip conversion without the need to suppress the warning (by using a nullable object and then ! when converting back)

The problem here is that U can be both null and not null. Given that the goal is to catch errors C# does the following:
when reading U it is assumed that the value can be null
when writing to U it is assumed that U could be non-nullable
Hence there is no possible workaround, since the casts can lead to errors in the general case. We just have additional information in the given scenario where we know this cannot happen.

Related

C# 8 gives a warning when returning a nullable generic with nullable constraint

This code:
public T Foo<T>()
where T : class?
{
return null;
}
Gives a following error:
A null literal introduces a null value when 'T' is a non-nullable
reference type
I don't see why we can't return null when we say that T can be nullable. If we additionally try to return T? we will get an error that T has to be non-nullable.
It seems it's kind of impossible to have a nullable constraint and return a nullable result at the same time.
Imagine you call:
string result = Foo<string>();
result now contains null. But it's a string, which is not nullable.
The compiler is warning you that Foo<T> may be called where T is not nullable, and returning null in this case would be unexpected.
Note that where T : class? means that T may be nullable, but it also might not be. Both string and string? are allowed. I don't believe there's any way to say "T must be nullable".
If you're trying to say:
T is allowed to be nullable
When T is non-nullable, then this type can still return null
Then you can write:
[return: MaybeNull]
public T Foo<T>()
where T : class?
{
return null!;
}
SharpLab
Note that MaybeNull only applies to the method's contract and not its body, when is why we need to return null!. However you can see in the SharpLab link above that the caller string result = Foo<string>(); gets the correct warning.
Let's start from basic. Let say you have a reference type variable that allows it to have null value. But your program design requires it to be not null all times, if a null value is encountered, it is mostly likely to get the NullReferenceException.
To avoid such design issues, The NullableReference types was introduced. Means if a reference type is allowed to have null you should mark it as Nullable and any usage should check for null value before using it. If checking is not found then compiler will generate a warning.
If you read this introductory article on NullableReferenceTypes You will get the fair idea of what is the intent of this new feature.

Is null in c# value type or reference type

Originally I had this impression that NULL is reference type because it is assigned to references, then encountered this concept called nullable value types, this makes my theory in an awkward situation, so is NULL value type of reference type on earth ?
The C# specification is clear on this point: the null keyword represent an expression that has no type. It is neither reference type nor value type nor pointer type. However that expression is convertible to all of those types. There is no requirement that the compiler classify all expressions as having a type, and in fact it does not.
Now you might then ask at runtime what is the type of a null reference, what is the type of a null value of nullable value type, and what is the type of a null pointer. The answers are:
a null reference does not have a type. If you have a box that can hold chocolates it is pointless to ask "what brand of chocolates are in this empty box?" A missing chocolate does not have a brand; a null reference does not have a type.
similarly, the type of a null pointer is, again, not anything; null pointer values don't point to anything. If you have a piece of paper that has the address 1600 Pennsylvania Avenue on it, you can ask "what is the colour of the house at this address", and get the answer: white. If you have a blank piece of paper, asking what the colour of the house at that address is doesn't get you any useful answer.
a null value of a nullable value type is a value of the nullable value type.
null isn't a type.
From msdn:
The null keyword is a literal that represents a null reference, one that does not refer to any object.
Nullable types are instances of the Nullable<T> struct which "Represents a value type that can be assigned null."
Neither. null is the default value for reference types, but it does not have a type itself.
Nullable value types are actually implemented as value types themselves - there are just compiler and library tricks to make them behave as expected when compared to null.
null doesn't have a type by itself in C#, as it was already said here. I think some precise examples showing the implications of this definition are missing from the other answers.
var something = null;
is invalid
string a = null; // valid. Null is implicitely casted to string.
int? b = null; // valid. Null is implicitely casted to Nullable<int>.
var c = "hello" // valid, type of c is infered by the compiler by looking at the right side of the expression.
var d = null; // not valid, compiler can't infer any type from null !
The invalid line in the above example will cause the following compilation error :
Error - CS0815 - Cannot assign to an implicitly-typed variable
and also :
Passing null to overloaded methods might require an explicit cast
void myMethod(int? myParam) { }
void myMethod(string myParam) { }
void test
{
string a = null;
myMethod(a); // no problem here, a is of type string so 2nd overload will be used.
myMethod(null); // not valid ! Compiler can't guess which one you want.
myMethod((string) null); // valid, null is casted to string so 2nd overload is to be used.
}
The invalid line in the above example will cause the following compilation error :
Error - CS0121 - The call is ambiguous between the following methods
or properties: 'myMethod(string)' and 'myMethod(int?)'
null does not reference to anything. It is just a type points to nothing. You people usually use it to de-reference an object.
The null keyword in C# does represent a null reference, so you can consider it a reference type (although technically it indicates not type).
The C# compiler and extra handling of null for use with nullable types. Nullable value types and value types and can never actually be null. When you assign or compare a Nullable value type to null the compiler will replace the code.
For example:
int? i;
if (i == null)
i = 1;
Is replaced by the compiler with:
int? i;
if (!i.HasVaue)
i = 1;

Nullable properties vs. Nullable local variables

I am puzzled by the following behavior of Nullable types:
class TestClass {
public int? value = 0;
}
TestClass test = new TestClass();
Now, Nullable.GetUnderlyingType(test.value) returns the underlying Nullable type, which is int.
However, if I try to obtain the field type like this
FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instance | BindingFlags.Public)[0];
and I invoke
Nullable.GetUnderlyingType(field.FieldType).ToString()
it returns a System.Nullable[System.Int32] type. So that means the method Nullable.GetUnderlyingType() has a different behavior depending on how you obtain the member type. Why is that so? If I simply use test.value how can I tell that it's Nullable without using reflection?
smartcaveman's answer is the best one here so far in that it actually identifies the section of the documentation that describes this behaviour.
The behaviour is undesirable and unfortunate; it is due to the behaviour in combination of three features which, by themselves, behave reasonably.
The three features are:
GetType is a non-virtual method; it cannot be overridden. This should make sense; an object doesn't get to decide what its type is reported as. By making it non-virtual, the method is guaranteed to tell the truth.
The this value passed to a non-virtual method declared on object must be converted to object; therefore in the case of objects of value type, the receiver of the call to GetType() is boxed to object.
Nullable value types have no boxed form; when you box a nullable int, you either get a boxed int or you get a null reference. You never get a valid reference to a boxed nullable int.
Each feature is reasonable on its own but in combination the result is undesirable: when you call GetType on a valid nullable int, the runtime boxes the nullable int to a boxed int and then passes that as the this of object.GetType which of course reports int. If the value is a null nullable int, the runtime boxes to null and then invokes GetType on a null reference and crashes. Neither of these behaviours are desirable, but we're stuck with them.
Nullable types are a little bit weird. However, at least their behavior is well documented.
From the C# programming guide on MSDN,
"How to: Identify Nullable Types"at http://msdn.microsoft.com/en-us/library/ms366789(VS.80).aspx :
You can also use the classes and methods of the System.Reflection namespace to generate Type objects that represent Nullable types. However, if you attempt to obtain type information from Nullable variables at runtime using the GetType method or the is operator, the result is a Type object that represents the underlying type, not the Nullable type itself.

Calling GetType on a Nullable type causes a boxing operation to be performed when the type is implicitly converted to Object. Therefore GetType always returns a Type object that represents the underlying type, not the Nullable type.
It's worth pointing out that the distinction in your headline is inaccurate. The type behavior is not distinct based on local variables or properties, but on whether the type is accessed via a runtime object or by reflection (or use of the typeof operator). Your inference is understandable, since the types of local variables are usually only accessed by means of a runtime object, however, it is flawed, because if you access a nullable object at runtime via a property accessor then its behavior will be equivalent to that of a local variable.
Also, to answer the last part of your question explicitly: the only way to tell that test.value is nullable without using reflection would be to access it and get a NullReferenceException (which, of course, can only happen if test.value is null. As written, in your example, the value is not null, so determining this without reflection would be impossible
The problem is that you're assuming that the Type of test.value is the same as the Type of the field.FieldType of value, and it's not. Getting the Type of test.value actually gets the Type of what is stored in the field, in this case a 0.
Type t = test.value.GetType()
is the same (in your example) as
Type t = 0.GetType()
To demonstrate, initialize value to null and test.value.GetType() will throw a NullReferenceException
Method GetNulllableUnderlyingType returns Underlying type for nullable types, for another types returns null;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetNulllableUnderlyingType(new TestClass().value));
Console.WriteLine(GetNulllableUnderlyingType(new TestClass()));
Console.ReadKey();
}
public class TestClass
{
public int? value;
}
public static Type GetNulllableUnderlyingType<T>(T value)
{
Type result = Nullable.GetUnderlyingType(typeof (T));
return result;
}
}
First of all, Nullable.GetUnderlyingType(test.value) in your example will not compile. Or typeof(test.value) as I have seen in comments.
Lets answer this by the following modified code:
TestClass test = new TestClass();
Type type1 = test.value.GetType(); // Gets the type of the data inside the field ==> System.Int32.
Type underlyingType1 = Nullable.GetUnderlyingType(type1); // Gets the underlying type of System.Int32 ==> null.
FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instance | BindingFlags.Public)[0];
Type type2 = field.FieldType; // Now the type is retrieved of the field, not the data inside. ==> System.Int32?
Type underlyingType2 = Nullable.GetUnderlyingType(type2); // Gets the underlying type of System.Int32? ==> System.Int32.
When you do a GetType() on a field, you will get the type of the data inside, not the type of the field itself. For example:
object o = 1;
Type type = o.GetType();
In this example, when you call GetType(), you also will get System.Int32, and not System.Object. Because in your your start with different types before calling GetUnderylingType, you end up with different results.

C# null - is it an object

When I was writing C# code a few days ago I noticed that the compiler complained that I had to cast null to a specific object.
Does this mean that null is simply an uninstantiated version of the type? Or is it a singleton value of a Null class like Java (even though it has special privileges)?
EDIT: an example of the code giving the error would be:
public String duplicate(String toDuplicate)
{
return toDuplicate + toDuplicate;
}
public String duplicate(int? toDuplicate)
{
String asString = toDuplicate.toString();
return asString + asString;
}
public static int Main(string[] args)
{
//This needs to be cast:
duplicate(null);
//To:
duplicate((string)null);
}
The reason I commented on null in Java was after reading this:
There is also a special null type, the
type of the expression null, which has
no name. Because the null type has no
name, it is impossible to declare a
variable of the null type or to cast
to the null type. The null reference
is the only possible value of an
expression of null type. The null
reference can always be cast to any
reference type. In practice, the
programmer can ignore the null type
and just pretend that null is merely a
special literal that can be of any
reference type.
Found here: Is null an Object?
I get the error you refer when i have overloaded methods and the compiler can't resolve which method to call at compile time. Is that it?
According to the MSDN description:
The null keyword is a literal that represents a null reference, one that does not refer to any object. null is the default value of reference-type variables. Ordinary value types cannot be null. However, C# 2.0 introduced nullable value types.
null is the "uninstanciated reference" for any type. It is not a value. null has no defined type.
No - the null is just a literal for the null reference.
The reason you need to "cast" it in this way (I put the word cast in quotes because you are not really casting an instance of anything), is purely to help the compiler resolve which overload of a method you are calling, or the method you are calling even exists.
In your example, you need to specify that you are calling the method "duplicate" that takes a single string argument. If you omit the cast, then the compiler only knows that the method you intended to call is called "duplicate" and has a single argument, but can't tell what type the argument is - did you mean to call duplicate(string) or did you mean to call duplicate(some other type)? Should it compile, or should it error telling you the method you are trying to call does not exist?
You will also get the same issue if you had a duplicate(byte[]) defined, because now your call is ambiguous without the explicit cast.
No its not an object. null is the default value of reference-type variables. Ordinary value types cannot be null. but there is another set called nullable types.
You are probably referring to the fact that the following leads to a compiler error:
int? nullableInt = (somecondition) ? value : null;
Indeed you need to add a cast here:
int? nullableInt = (somecondition) ? value : (int?)null;
Even though I'm not able to explain this in detail, I'd suspect the following:
int? is actually a short form for Nullable<int>, so it is basically an object instance. When assigning an int value to nullableInt, a property of Nullable<int> will be set internally. Directly assigning null would also be ok.
The conditional assignment however, would return two different types: int in case somecondition is true and object (null) otherwise.
Now the compiler doesn't know how to handle this, as the ternary operator needs to return values of the same type. So you need to specify the desired "type for the null value".
Sorry if this is not a very deep technical explanation - I'm sure there's somebody who can elaborate this better, but it might help understand this better.

Why can't I use the as keyword for a struct?

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?
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.
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.
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;

Categories

Resources