A default( T ) enum value doesn't equal null - c#

I'm a bit surprised to find that calling the constructor in:
class MyClass<T>
{
public MyClass()
{
if ( default( T ) == null )
Debugger.Break();
}
}
doesn't break when T is an enum type. Why? It seems that even for enums, default( T ) and null should be equivalent.

No value type is ever going to test as equal to null, unless it's Nullable<T> which has special compiler and language support to treat an unset value as null.
The default(T) where T is any value type, including an enum, is going to be whatever the 0-filled value for that type is. I.e. an actual value. Not null.
Additional reading:
How to set enum to null
What does default(object); do in C#?
You may also want to read through some of the other hits in this search: [c#] default value enum null

Because enum in .net is a value type. If you only want to find out if default(T) == null then you can check if it is a reference type instead.
class MyClass<T>
{
public MyClass()
{
if (typeof (T).IsClass)
{
Debugger.Break();
}
else if (typeof (T).IsValueType)
{
//do something
}
}
}

Related

Implicit operator isn't called for default of struct in C#

I'm implementing a C# variant of Haskell's Maybe and came across a weird issue where null and default has different implication on the value returned from an implicit conversion.
public class TestClass
{
public void Test()
{
Maybe<string> valueDefault = default;
Maybe<string> valueNull = null;
Maybe<string> valueFoobar = "foobar";
Console.WriteLine($"Default: (Some {valueDefault.Some}, None {valueDefault.None}");
Console.WriteLine($"Null: (Some {valueNull.Some}, None {valueNull.None}");
Console.WriteLine($"Foobar: (Some {valueFoobar.Some}, None {valueFoobar.None}");
}
}
public struct Maybe<T>
{
public T Some { get; private set; }
public bool None { get; private set; }
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>() {
Some = value,
None = value == null
};
}
}
The output being:
Default: (Some , None False)
Null: (Some , None True)
Foobar: (Some foobar, None False)
I was expecting both valueDefault and valueNull to be equal. But seems that null is converted while default isn't. I fixed the issue by replacing None with HasSome with a reversed boolean condition, but still the question remains.
Why is null and default treated differently?
Every type has a default value, including Maybe<T>. See this page for a list.
Maybe<string> valueDefault = default; will assign the default value of Maybe<string> to valueDefault. What's the default value of Maybe<string>? According to that page, since Maybe<string> is a struct, its default value is:
The value produced by setting all value-type fields to their default values and all reference-type fields to null.
So it's an instance of Maybe<string> with Some being null and None being false. false is the default value of bool.
The compiler doesn't try to use the default value of string, since that requires a further conversion to Maybe<string>. If it can just use the default value of Maybe<string>, why go the extra trouble, right?
You can force it to though:
Maybe<string> valueDefault = default(string);
null, on the other hand, gets converted to Maybe<string> because null is not a valid value of Maybe<string> (structs can't be null!), so the compiler deduces that you must mean null as string, and does the implicit conversion.
You might know this already, but you seem to be reinventing Nullable<T>
default always fills the memory of the struct with zero bytes. null is not a valid value for a value type, so the compiler discovers the implicit (Maybe<string>)(string)null cast.
Perhaps you could replace with;
public struct Maybe<T>
{
public T Some { get; private set; }
public bool None => Some == null;
...

Type checking on Nullable<int>

If have the following method:
static void DoSomethingWithTwoNullables(Nullable<int> a, Nullable<int> b)
{
Console.WriteLine("Param a is Nullable<int>: " + (a is Nullable<int>));
Console.WriteLine("Param a is int : " + (a is int));
Console.WriteLine("Param b is Nullable<int>: " + (b is Nullable<int>));
Console.WriteLine("Param b is int : " + (b is int));
}
When i call this method with null as a parameter, the type check returns false for this parameter. For example this code
DoSomethingWithTwoNullables(5, new Nullable<int>());
results in this output:
Param a is Nullable<int>: True
Param a is int : True
Param b is Nullable<int>: False
Param b is int : False
Is there any way to preserve the type information when using a Nullable and passing null? I know that checking the type in this example is useless, but it illustrates the problem. In my project, I pass the parameters to another method that takes a params object[] array and tries to identify the type of the objects. This method needs to do different things for Nullable<int> and Nullable<long>.
Going straight to the underlying problem, no, you can't do this. A null Nullable<int> has exactly the same boxed representation as a null Nullable<long>, or indeed a 'normal' null. There is no way to tell what 'type' of null it is, since its underlying representation is simply all-zeros. See Boxing Nullable Types for more details.
conceptually, new Nullable<int> is null.
If we generalise, forgetting about Nullable<T>:
string s = null;
bool b = s is string;
we get false. false is the expected value for a type-check on a null value.
You can try using Reflection to achieve this. Relevant article here.
Unfortunately, null does not point to any specific memory location, and thus there is no metadata that you can associate with it to lookup the type. Thus, you cannot gain any additional information about the variable.
Unless I'm misunderstanding the question, you can get the type using GetType(). For example,
int? myNullableInt = null;
Console.WriteLine(myNullableInt.GetValueOrDefault().GetType());
If myNullableInt is null, a default value will be returned. Check the type of this returned value and, in this case, it will return System.Int32. You can do an If..else/Switch check on the returned type to perform the relevant action.
(int? is the same as Nullable<int>)
You can't do this, nor should you want to. Since Nullable<T> is a struct, value-type variables of this type have all the type information you need at compile time. Just use the typeof operator.
On the other hand, you might have a Nullable instance whose type you don't know at compile time. That would have to be a variable whose static type is object or some other reference type. Howver, because a Nullable<T> value boxes to a boxed T value, there's no such thing as a boxed Nullable<T>. That instance whose type your checking will just be a T.
This is why you get the same result for is int and is Nullable<int>. There's no way to distinguish between a boxed int and a boxed int?, because there is no boxed int?.
See Nulls not missing anymore for details.
As it has already been pointed out null has no type. To figure out if something is int? vs long? you need to use reflection to get information about something storing the type. Here is some code that you may be able to use as inspiration (not knowing exactly what you try to achieve the code is a bit weird):
class Pair<T> where T : struct {
public Pair(T? a, T? b) {
A = a;
B = b;
}
public T? A { get; private set; }
public T? B { get; private set; }
}
void DoSomething<T>(Pair<T> pair) where T : struct {
DoMore(pair);
}
void DoMore(params object[] args) {
Console.WriteLine("Do more");
var nullableIntPairs = args.Where(IsNullableIntPair);
foreach (Pair<int> pair in nullableIntPairs) {
Console.WriteLine(pair.A);
Console.WriteLine(pair.B);
}
}
bool IsNullableIntPair(object arg) {
var type = arg.GetType();
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(Pair<>)
&& type.GetGenericArguments()[0] == typeof(int);
}
If you execute the following code
DoSomething(new Pair<int>(5, new int?()));
DoSomething(new Pair<long>(new long?(), 6L));
you get the following output:
Do more
5
null
Do more
You can use typeof :
a == typeof(Nullable<int>) //true
a == typeof(int) //false

What are Nullable Types in C#?

int? _fileControlNo = null;
public int? FileControlNo
{
get { return _fileControlNo; }
set { _fileControlNo = value; }
}
I'm getting a syntax error when I assign null values to the above properties.
objDPRUtils.FileControlNo =sArrElements.Value(3)==null ? null : Convert.ToInt32(sArrElements.Value(3));
Please, can anyone explain to me why the error occurs if I'm able to set null value in valuetype object using Nullable Type.
The results of the conditional operators need to be of the same type or types that can be implicitly convertible to each other.
In your case you have a null and an Int32 - these violate that requirement.
If instead of an Int32 you return a nullable Int32, the null can be implicitly converted to this type and your code will work (or alternatively, cast the null to an int?).
Cast your null to int?
objDPRUtils.FileControlNo =sArrElements.Value(3)==null ? (int?) null : Convert.ToInt32(sArrElements.Value(3));
The conditional operator needs to return result of the same type and in your case its not possible for null
this should work
sArrElements.Value(3)==null ? (int?)null : Convert.ToInt32(sArrElements.Value(3));
Nullable types are exactly what they say they are: simple value types that can also store a null value.
I would suggest that you do not need the last line of code at all. you should be able to get away with:
objDPRUtils.FileControlNo =sArrElements.Value(3);
If you really want to assign another value in case of null, use the null coalescing operator ??
objDPRUtils.FileControlNo =sArrElements.Value(3)??0;
which in this case, would assign the value 0 to the FileControlNo in case of the right hand side being null.
Try to assign directly like this :
objDPRUtils.FileControlNo =sArrElements.Value(3);
Your code mixes types null and Int32 cannnot be mixed in this instance like that. They need to be of the same type.
If you use int.TryParse and only attempt to set the value on success you will achieve the same result and can use HasValue on the field to determine if its null or not which is how nullable types are used typically
Silly example
class Program
{
private static int? _fileControlNo;
static void Main(string[] args)
{
string[] sArrElements = new string[] { "1", "2", "3", null };
int result;
if (int.TryParse(sArrElements[3], out result))
{
FileControlNo = result;
}
if (_fileControlNo.HasValue)
{
// do something here
}
}
public static int? FileControlNo
{
get { return _fileControlNo; }
set { _fileControlNo = value; }
}
}
you will note that your code inside the test for HasValue in this case will never execute because _fileControlNo cannot be set because tryParse always fails (change the indexer and that will change).

How can I check if a value can be cast to a generic type?

I have a method wrapping some external API call which often returns null. When it does, I want to return a default value. The method looks like this
public static T GetValue<T>(int input)
{
object value = ExternalGetValue(input);
return value != null ? (T)value : default(T)
}
The problem is that (T)value might throw an invalid cast exception. So I thought I would change it to
var value = ExternalGetValue(input) as Nullable<T>;
but this requires where T : struct, and I want to allow reference types as well.
Then I tried adding an overload which would handle both.
public static T GetValue<T>(int input) where T : struct { ... }
public static T GetValue<T>(int input) where T : class { ... }
but I found you can't overload based on constraints.
I realize I can have two methods with different names, one for nullable types and one for nonnullable types, but I'd rather not do that.
Is there a good way to check if I can cast to T without using as? Or can I use as and have a single method which works for all types?
You can use is:
return value is T ? (T)value : default(T);
(Note that value is T will return false if value is null.)

"Error CS0118: 'field' used as a 'type'" with a Type field

I'm writing a class that holds a variable of any type. To do so, I store both the variable (as an object reference) and its Type. When I try to cast the object back to the correct type, though, I get error CS0118, because I'm using a field (which is of type Type) as a type.
Here is my class:
public class Node
{
Type m_oType = null;
public Type Type
{
get { return m_oType; }
set { m_oType = value; }
}
object m_oValue = null;
public object Value
{
get { return m_oValue; }
set
{
if (m_oValue == null)
{
if (value is m_oType) // ERROR CS0118
{
m_oValue = value;
}
}
}
}
}
I've tried to search online for a way to do this (i.e., using cast operators, as and is), but I keep getting the same basic tutorials about casting variables. Can someone give me a pointer as to how I can achieve this? Thanks.
I suspect you want something like this (but read on):
if (m_oType.IsAssignableFrom(value.GetType()))
Note that we're calling it on m_oType, not passing m_oType to it. From the docs on IsAssignableFrom's return value, where c is the parameter:
true if c and the current Type represent the same type, or if the current Type is in the inheritance hierarchy of c, or if the current Type is an interface that c implements, or if c is a generic type parameter and the current Type represents one of the constraints of c.
For example, typeof(object).IsAssignableFrom(typeof(string)) returns true because object is in the inheritance hierarchy of string.
EDIT: As noted, that will break if either m_oType is null or value is null. We can get around value being null easily enough, but it's not clear what you'd expect it to do if m_oType is null. Perhaps you should prevent that in the setter for the Type property (and the constructor)? Then use either:
// This will always store a null
if (value == null || m_oType.IsAssignableFrom(value.GetType()))
// This will never store a null
if (value != null && m_oType.IsAssignableFrom(value.GetType()))
Something like
if (m_oType.IsAssignableFrom(value.GetType()))
But I'm pretty sure you should have a look at Generics.
public class Node<T>
{
T m_oValue = null;
public T Value
{
get { return m_oValue; }
set
{
if (m_oValue == null)
{
m_oValue = value;
}
}
}
}

Categories

Resources