C# Suppress nullable after custom thrower method, like ArgumentNullException.ThrowIfNull [duplicate] - c#

I'm using the new nullable reference types from C# 8 and I was wondering if it is possible to indicate that a parameter passed in is not null if the method returns at all.
I've found [NotNullIf] and [DoesNotReturnIf] but these appear to trigger off a method's return value and a specific bool parameter respectively.
Here is what I currently have:
public static bool RaiseIfNull([NotNullWhen(true)] object? thing) => RaiseIf(() => thing is null);
public static bool RaiseIf(Func<bool> predicate)
{
if (predicate()) throw new HttpException(400);
return true;
}
This seems good, but then when I call it - I still see warnings. (I also tried RaiseIfNull([NotNullIfNotNull("thing")] object? thing) and that didn't work.)
[HttpPost("{id}")]
public async Task Etc(string id, [FromBody] Dto data)
{
HttpException.RaiseIfNull(data?.property);
await DoEtc(id, data.property)); // warning here
}
Am I missing something obvious?

Using a normal null-check
First of all, RaiseIfNull doesn't offer something more than :
var value=data?.property ?? new HttpException(400);
Which the compiler can recognize. RaiseIfNull on the other hand hides what's actually going on. This code doesn't produce a warning:
class D
{
public string? property{get;set;}
}
D? data=null;
var value=data?.property ?? throw new HttpException(400);
Console.WriteLine(value.Length);
Unconditional Post-Condition and generics
That said, the the correct argument to use is NotNull - after the method executes, the parameter is not null even if the type itself is nullable. That attribute can be applied to :
Parameters
Properties
Fields and
Return values
The methods can be made generic too, to avoid boxing structs into objects. To do that we need to specify whether the type is a class or struct, because the resulting concrete types are very different - a string? is still a string while an int? is a Nullable<int> :
public static bool RaiseIfNull<T>([NotNull] T? thing)
where T:class
=> RaiseIf(() => thing is null);
public static bool RaiseIfNull<T>([NotNull] T? thing)
where T:struct
=> RaiseIf(() => thing is null);
Given those methods, the following code won't generate a warning either :
D? data=null;
RaiseIfNull(data?.property);
Console.WriteLine(data.property.Length);
Finally, we can get rid of the return value:
public static void RaiseIfNull<T>([NotNull] T? thing)
where T:class
=> RaiseIf(() => thing is null);
public static void RaiseIfNull<T>([NotNull] T? thing)
where T:struct
=> RaiseIf(() => thing is null);

Related

How to write generic method for casting DBNull value to Nullable?

I have a database with a nullable column foo_date, where Npgsql maps the sql NULL value to an instance of the C# class DBNull. In my C# aggregate I use the type DateTime? for said column. So the question is how to easily convert DBNull to a nullable type.
I want to write a utility method like, e.g.,
public static class DbUtil
{
public static T? CastToNullable<T>(object obj)
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
}
which I would like to use like this:
IDataRecord rec = ...
DateTime? fooDate = DbUtil.CastToNullable<DateTime>(rec["foo_date"]);
However, I get the compiler error:
Error CS0403 Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
When I replace return null by return default(T?), the compiler is happy, but the method does not return null but the default Date, i.e., 01.01.0001.
What is the correct way to write the generic utility method above?
I'm assuming you've got nullable reference types enabled, otherwise that T? would be a compiler error.
T?, where T is a generic type parameter, has multiple meanings unfortunately.
When T is a value type (i.e. you have a where T : struct constraint), T? means Nullable<T>.
When T is a reference type (i.e. you have a where T : class constraint), T? means a reference type which is allowed to be null.
When T is unconstrained, T? means "If T is a reference type, then this is allowed to be null; otherwise no effect".
In other words, if you have:
T? Foo<T>() => default;
If you call Foo<int>(), you get back an int, not an int?.
If however you have:
T? Foo<T>() where T : struct => default;
then Foo<int>() returns an int?.
In other words, your signature needs to be:
public static T? CastToNullable<T>(object obj) where T : struct
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
Please check Nullable structure which is in fact long/real version of T?
In that case your method would be
public static T? CastToNullable<T>(object obj) where T: struct
{
if (DBNull.Value.Equals(obj))
return null;
return new Nullable<T>((T)obj);
}
That would work only for structures, for reference types you could add
public static T CastToNullableObj<T>(object obj) where T: class
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
It seems like you want to access columns on your NpgsqlDataReader, but to get .NET null for null columns instead of DBNull.Value. If so, the usual way is to write use an extension method as follows:
public static class DbDataReaderExtensions
{
public static T? GetValueOrDefault<T>(this DbDataReader reader, int ordinal)
where T : class
=> reader.IsDBNull(ordinal) ? null : reader.GetFieldValue<T>(ordinal);
}
This can be used directly on your reader:
var s = reader.GetValueOrDefault<string>(0);

Is there a way to hint to C# how to perform generic type inference?

I am attempting to add compile time checks to code which previously took the string name of a data object's property and a value of type 'object'. I am doing this in order to ensure the property and value are actually of the same type to prevent cases where they are not from introducing a runtime bug.
I am handling the compile time check by creating a method which takes an expression of type Expression<Func<TDataObject, TPropertyValue>> and a parameter of type TPropertyValue. From this, I can inspect the expression to get the name of the property that would be returned and then use the same logic we have today with strings and value as object type.
public interface IPropertyDictionary<TDataObject> where TDataObject : class
{
void AddIfSameType<TProperty>(
Expression<Func<TDataObject, TProperty>> propertyAccessExpression,
TProperty propertyValue);
}
The following works as expected:
// Allowed
propDictionary.AddIfSameType(e => e.IntProperty, 123);
// Flagged by intellisense (though the expression is flagged rather than second parameter...)
propDictionary.AddIfSameType(e => e.IntProperty, "asdf");
However, this does not work as expected:
// Not flagged as error
propDictionary.AddIfSameType(e => e.IntProperty, 123L);
With that, C# infers TPropertyValue to be a long rather than an int. In the debugger, I can see that the expression is being transformed to cast it to a long:
e => Convert(e.IntProperty)
In my ideal case, C# would prefer the type of IntProperty when making it's type inference and raise a compile time error indicating casting from long to int requires an explicit cast. Is there are way to indicate to C# that it should only use the first parameter of the method when inferring the type? The only alternative I have at the moment is to explicitly provide the type parameter:
// Flagged by intellisense
propDictionary.AddIfSameType<int>(e => e.IntProperty, 123L);
But in the 99% case people will not pass a type parameter and I would not expect them to realize they need to do so in this situation. As a result, their bug again becomes one of the runtime errors that I am eager to avoid.
Yes, in a slightly roundabout way. Use two type parameters, and a type parameter constraint. There isn't a equality constraint, but the inheritance constraint should work for most scenarios:
static void AddIfSameType<TLProp,TRProp>(Func<DataObject,TLProp> lprop, TRProp rprop) where TRProp : TLProp
{
}
static void Main(string[] args)
{
AddIfSameType(d => d.IntProperty, 1);
//compiles
AddIfSameType(d => d.IntProperty, 1L);
//Error CS0315 The type 'long' cannot be used as type parameter 'TRProp'
//... There is no boxing conversion from 'long' to 'int'.
}
There's not a way to do what you're describing while preserving the exact syntax you're asking for. (I stand corrected: see David Browne's answer for a way to do it.)
If you're willing to change your approach, you could separate the method call with the expression from the one with the value. Something like this:
propDictionary.AdderFor(e => e.IntProperty).Add(123L);
One potential benefit of this approach is that you could capture the "adder" as a variable.
var intAdder = propDictionary.AdderFor(e => e.IntProperty)
intAdder.Add(456); // okay
intAdder.Add(123L); // error
How is C# supposed to know that TDataObject has a property IntProperty? All you say is that TDataObject must be a class with where TDataObject : class. You must specify a constraint that lets C# know about this property (and possibly others). E.g.
public interface IProperties
{
int IntProperty { get; set; }
double DoubleProperty { get; set; }
string StringProperty { get; set; }
}
public interface IPropertyDictionary<TDataObject> where TDataObject : IProperties
{
void AddIfSameType<TProperty>(
Expression<Func<TDataObject, TProperty>> getProp, TProperty value);
}
Then you can declare a dictionary with
public class PropDictionary<TDataObject> : IPropertyDictionary<TDataObject>
where TDataObject : IProperties
{
public void AddIfSameType<TProperty>(
Expression<Func<TDataObject, TProperty>> getProp, TProperty value)
{
}
}
and a data class
public class DataObject : IProperties
{
public int IntProperty { get; set; }
public double DoubleProperty { get; set; }
public string StringProperty { get; set; }
}
Now both of these calls work
var propDictionary = new PropDictionary<DataObject>();
propDictionary.AddIfSameType(e => e.DoubleProperty, 123);
propDictionary.AddIfSameType(e => e.IntProperty, 123L);
Why do they work?
The first one because the types are inferred to be
void ProperDictionary<DataObject>.AddIfSameType<double>(
Expression<Func<DataObject, double>> getProp, double value)
The int value passed is simply cast to double.
The second case is a bit surprising. The types are inefrred to be
void ProperDictionary<DataObject>.AddIfSameType<long>(
Expression<Func<DataObject, long>> getProp, longvalue)
I assume that the return value is implicitly widened:
e => (long)e.IntProperty
Conclusion: C# more clever than you might think. It infers the generic types and automatically casts values to make it work where possible.
UPDATE
Therefore use two distinct type parameters for the Func return type and the value.
public void AddIfSameType<TProp, TValue>(
Expression<Func<TDataObject, TProp>> getProp, TValue value)
{
if (typeof(TProp) == typeof(TValue)) {
} else {
}
}
C# will infer the exact type for each of these type parameters. You could also type the value as object instead, but with the cost of boxing of value types.

Implicit conversion fails when changing struct to sealed class

Struct/class in question:
public struct HttpMethod
{
public static readonly HttpMethod Get = new HttpMethod("GET");
public static readonly HttpMethod Post = new HttpMethod("POST");
public static readonly HttpMethod Put = new HttpMethod("PUT");
public static readonly HttpMethod Patch = new HttpMethod("PATCH");
public static readonly HttpMethod Delete = new HttpMethod("DELETE");
private string _name;
public HttpMethod(string name)
{
// validation of name
_name = name.ToUpper();
}
public static implicit operator string(HttpMethod method)
{
return method._name;
}
public static implicit operator HttpMethod(string method)
{
return new HttpMethod(method);
}
public static bool IsValidHttpMethod(string method)
{
// ...
}
public override bool Equals(object obj)
{
// ...
}
public override int GetHashCode()
{
return _name.GetHashCode();
}
public override string ToString()
{
return _name;
}
}
The following code triggers the issue:
public class HttpRoute
{
public string Prefix { get; }
public HttpMethod[] Methods { get; }
public HttpRoute(string pattern, params HttpMethod[] methods)
{
if (pattern == null) throw new ArgumentNullException(nameof(pattern));
Prefix = pattern;
Methods = methods ?? new HttpMethod[0];
}
public bool CanAccept(HttpListenerRequest request)
{
return Methods.Contains(request.HttpMethod) && request.Url.AbsolutePath.StartsWith(Prefix);
}
}
The compiler error is created by changing the HttpMethod struct into a sealed class. The error is reported for return Methods.Contains(request.HttpMethod), note: request.HttpMethod in this case is a string. Which produces the following:
Error CS1929 'HttpMethod[]' does not contain a definition for 'Contains' and the best extension method overload 'Queryable.Contains<string>(IQueryable<string>, string)' requires a receiver of type 'IQueryable<string>'
My question is why? I can redesign the code to make it work, but I'm wanting to know why changing from struct to sealed class creates this weird error.
Edit: Adding a simplified set of example code (available here: https://dotnetfiddle.net/IZ9OXg). Take note that commenting out the implicit operator to string on the second class allows the code to compile:
public static void Main()
{
HttpMethod1[] Methods1 = new HttpMethod1[10];
HttpMethod2[] Methods2 = new HttpMethod2[10];
var res1 = Methods1.Contains("blah"); //works
var res2 = Methods2.Contains("blah"); //doesn't work
}
public struct HttpMethod1
{
public static implicit operator HttpMethod1(string method)
{
return new HttpMethod1();
}
public static implicit operator string (HttpMethod1 method)
{
return "";
}
}
public class HttpMethod2
{
public static implicit operator HttpMethod2(string method)
{
return new HttpMethod2();
}
//Comment out this method and it works fine
public static implicit operator string (HttpMethod2 method)
{
return "";
}
}
Things I know:
Plainly the problem is in type inference.
In the first case, T is deduced to be HttpMethod1.
In the struct case, there is no conversion from HttpMethod1[] to IEnumerable<string> because covariance only works on reference types.
In the class case, there is no conversion from HttpMethod2[] to IEnumerable<string> because covariance only works on reference conversions, and this is a user-defined conversion.
Things I suspect but need to confirm:
Something about the slight difference between my last two points is confusing the type inference algorithm.
UPDATE:
It has nothing to do with covariant array conversions. The problem repros even without array conversions.
It does however have to do with covariant interface conversions.
It has nothing to do with strings. (Strings are often a bit weird because they have a hard-to-remember conversion to IEnumerable<char> that occasionally messes up type inference.)
Here's a program fragment that displays the problem; update your conversions to convert to C instead of string:
public interface IFoo<out T> {}
public class C {}
public class Program
{
public static bool Contains<T>(IFoo<T> items, T item)
{
System.Console.WriteLine(typeof(T));
return true;
}
public static void Main()
{
IFoo<HttpMethod1> m1 = null;
IFoo<HttpMethod2> m2 = null;
var res1 = Contains(m1, new C()); //works
var res2 = Contains(m2, new C()); //doesn't work
}
}
This looks like a possible bug in type inference, and if it is, it is my fault; many apologies if that is the case. Sadly I do not have time to look into it further today. You might want to open an issue on github and have someone who still does this for a living look into it. I would be fascinated to learn what the result was, and if it turns out to be a bug in either the design or the implementation of the inference algorithm.
Firstly, this is an observed behavioural difference between structs and classes. The fact that you have 'sealed' your class does not affect the outcome in this scenario.
Also we know the following statement will compile as expected for HttpMethod type declared as both a struct and class, thanks to the implicit operator.
string method = HttpMethods[0];
Dealing with Arrays introduces some lesser understood compiler nuances.
Covariance
When HttpMethod is a class (reference type), with an array such as HttpRoute.HttpMethods Array covariance (12.5 C# 5.0 Language Spec) comes into play that allows HttpMethod[x] to be treated as an object. Covariance will respect inbuilt implicit reference conversions (such as type inheritance or conversion to object) and it will respect explicit operators, but it will not respect or look for user defined implicit operators. (While a bit ambigous the actual spec doc lists specifically default implicit operators and explicit operators, it does not mention the user defined operators but seeing everything else is so highly specified you can infer that user defined operators are not supported.)
Basically Covariance takes precedence over many generic type evaluations. More on this in a moment.
Array covariance specifically does not extend to arrays of value-types. For example, no conversion exists that permits an int[] to be treated as an object[].
So when HttpMethod is a struct (value type), covariance is no longer an issue and the following generic extension from System.Linq namespace will apply:
public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value);
Because you have passed in a string comparator, the Contains statement will be evaluated as follows:
public static bool Contains<string>(this IEnumerable<string> source, string value);
When HttpMethod is a class (Reference Type), thanks to covariance, HttpMethod[] in it's current form comparable only with Object[] and thus IEnumerable, but not IEnumerable< T >, Why not? because the compiler needs to be able to determine the type to generate the generic implementation of IEnumerable< T > and to determine if it can perform an explicit cast from object to T.
Put another way, Compiler cannot determine if T can definetly be a String or not, so it doesn't find the match in the Linq extension methods that we were expecting.
So what can you do about it? (! Not this !)
The first common attempt might be to try using .Cast< string >() to cast the HttpMethod instances to strings for the comparison:
return HttpMethods.Cast<string>().Contains(request.Method) && request.Url.AbsolutePath.StartsWith(Prefix);
You will find that this does not work. Even though The parameter for Cast< T > is of type IEnumerable, not IEnumerable< T >. It is provided to allow you to use older collections that do not implement the generic version of IEnumerable with LINQ. Cast< T > is only designed to convert non-generic objects to their "true" type through the process of evaluating common origins for reference types or Un-Boxing for value types. If Boxing and Unboxing (C# Programming Guide) only applies to value types (structs) and since our HttpMethod type is a reference type (class) the only common origin between HttpMethod and String is Object. On HttpMethod there is no implicit, or even explicit operator that accepts Object and as it is not a value type there is no in built un-box operator that the compiler can use.
Note that this Cast<> will fail at runtime in this scenario when HttpMethod is a value type (class) the compiler will be happy to let it build.
Final Workaround
Instead of Cast< T > or relying on implicit conversions we will need to force the elements in the HttpMethods array to be explicitly cast to string (This will still use out implicit operator!) but Linq again makes this a trivial, but necessary task:
return HttpMethods.Select(c => (string)c).Contains(request.Method) && request.Url.AbsolutePath.StartsWith(Prefix);

How to compare <T> value with dafault<T> (C#) [duplicate]

I have a generic method defined like this:
public void MyMethod<T>(T myArgument)
The first thing I want to do is check if the value of myArgument is the default value for that type, something like this:
if (myArgument == default(T))
But this doesn't compile because I haven't guaranteed that T will implement the == operator. So I switched the code to this:
if (myArgument.Equals(default(T)))
Now this compiles, but will fail if myArgument is null, which is part of what I'm testing for. I can add an explicit null check like this:
if (myArgument == null || myArgument.Equals(default(T)))
Now this feels redundant to me. ReSharper is even suggesting that I change the myArgument == null part into myArgument == default(T) which is where I started. Is there a better way to solve this problem?
I need to support both references types and value types.
To avoid boxing, the best way to compare generics for equality is with EqualityComparer<T>.Default. This respects IEquatable<T> (without boxing) as well as object.Equals, and handles all the Nullable<T> "lifted" nuances. Hence:
if(EqualityComparer<T>.Default.Equals(obj, default(T))) {
return obj;
}
This will match:
null for classes
null (empty) for Nullable<T>
zero/false/etc for other structs
How about this:
if (object.Equals(myArgument, default(T)))
{
//...
}
Using the static object.Equals() method avoids the need for you to do the null check yourself. Explicitly qualifying the call with object. probably isn't necessary depending on your context, but I normally prefix static calls with the type name just to make the code more soluble.
I was able to locate a Microsoft Connect article that discusses this issue in some detail:
Unfortunately, this behavior is by design and there is not an easy solution to enable use of with type parameters that may contain value types.
If the types are known to be reference types, the default overload of defined on object tests variables for reference equality, although a type may specify its own custom overload. The compiler determines which overload to use based on the static type of the variable (the determination is not polymorphic). Therefore, if you change your example to constrain the generic type parameter T to a non-sealed reference type (such as Exception), the compiler can determine the specific overload to use and the following code would compile:
public class Test<T> where T : Exception
If the types are known to be value types, performs specific value equality tests based on the exact types used. There is no good "default" comparison here since reference comparisons are not meaningful on value types and the compiler cannot know which specific value comparison to emit. The compiler could emit a call to ValueType.Equals(Object) but this method uses reflection and is quite inefficient compared to the specific value comparisons. Therefore, even if you were to specify a value-type constraint on T, there is nothing reasonable for the compiler to generate here:
public class Test<T> where T : struct
In the case you presented, where the compiler does not even know whether T is a value or reference type, there is similarly nothing to generate that would be valid for all possible types. A reference comparison would not be valid for value types and some sort of value comparison would be unexpected for reference types that do not overload.
Here is what you can do...
I have validated that both of these methods work for a generic comparison of reference and value types:
object.Equals(param, default(T))
or
EqualityComparer<T>.Default.Equals(param, default(T))
To do comparisons with the "==" operator you will need to use one of these methods:
If all cases of T derive from a known base class you can let the compiler know using generic type restrictions.
public void MyMethod<T>(T myArgument) where T : MyBase
The compiler then recognizes how to perform operations on MyBase and will not throw the "Operator '==' cannot be applied to operands of type 'T' and 'T'" error that you are seeing now.
Another option would be to restrict T to any type that implements IComparable.
public void MyMethod<T>(T myArgument) where T : IComparable
And then use the CompareTo method defined by the IComparable interface.
Try this:
if (EqualityComparer<T>.Default.Equals(myArgument, default(T)))
that should compile, and do what you want.
(Edited)
Marc Gravell has the best answer, but I wanted to post a simple code snippet I worked up to demonstrate it. Just run this in a simple C# console app:
public static class TypeHelper<T>
{
public static bool IsDefault(T val)
{
return EqualityComparer<T>.Default.Equals(obj,default(T));
}
}
static void Main(string[] args)
{
// value type
Console.WriteLine(TypeHelper<int>.IsDefault(1)); //False
Console.WriteLine(TypeHelper<int>.IsDefault(0)); // True
// reference type
Console.WriteLine(TypeHelper<string>.IsDefault("test")); //False
Console.WriteLine(TypeHelper<string>.IsDefault(null)); //True //True
Console.ReadKey();
}
One more thing: can someone with VS2008 try this as an extension method? I'm stuck with 2005 here and I'm curious to see if that would be allowed.
Edit: Here is how to get it working as an extension method:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// value type
Console.WriteLine(1.IsDefault());
Console.WriteLine(0.IsDefault());
// reference type
Console.WriteLine("test".IsDefault());
// null must be cast to a type
Console.WriteLine(((String)null).IsDefault());
}
}
// The type cannot be generic
public static class TypeHelper
{
// I made the method generic instead
public static bool IsDefault<T>(this T val)
{
return EqualityComparer<T>.Default.Equals(val, default(T));
}
}
To handle all types of T, including where T is a primitive type, you'll need to compile in both methods of comparison:
T Get<T>(Func<T> createObject)
{
T obj = createObject();
if (obj == null || obj.Equals(default(T)))
return obj;
// .. do a bunch of stuff
return obj;
}
Extension method based on accepted answer.
public static bool IsDefault<T>(this T inObj)
{
return EqualityComparer<T>.Default.Equals(inObj, default);
}
Usage:
private bool SomeMethod(){
var tValue = GetMyObject<MyObjectType>();
if (tValue == null || tValue.IsDefault()) return false;
}
Alternate with null to simplify:
public static bool IsNullOrDefault<T>(this T inObj)
{
if (inObj == null) return true;
return EqualityComparer<T>.Default.Equals(inObj, default);
}
Usage:
private bool SomeMethod(){
var tValue = GetMyObject<MyObjectType>();
if (tValue.IsNullOrDefault()) return false;
}
There is going to be a problem here -
If you're going to allow this to work for any type, default(T) will always be null for reference types, and 0 (or struct full of 0) for value types.
This is probably not the behavior you're after, though. If you want this to work in a generic way, you probably need to use reflection to check the type of T, and handle value types different than reference types.
Alternatively, you could put an interface constraint on this, and the interface could provide a way to check against the default of the class/struct.
I think you probably need to split this logic into two parts and check for null first.
public static bool IsNullOrEmpty<T>(T value)
{
if (IsNull(value))
{
return true;
}
if (value is string)
{
return string.IsNullOrEmpty(value as string);
}
return value.Equals(default(T));
}
public static bool IsNull<T>(T value)
{
if (value is ValueType)
{
return false;
}
return null == (object)value;
}
In the IsNull method, we're relying on the fact that ValueType objects can't be null by definition so if value happens to be a class which derives from ValueType, we already know it's not null. On the other hand, if it's not a value type then we can just compare value cast to an object against null. We could avoid the check against ValueType by going straight to a cast to object, but that would mean that a value type would get boxed which is something we probably want to avoid since it implies that a new object is created on the heap.
In the IsNullOrEmpty method, we're checking for the special case of a string. For all other types, we're comparing the value (which already know is not null) against it's default value which for all reference types is null and for value types is usually some form of zero (if they're integral).
Using these methods, the following code behaves as you might expect:
class Program
{
public class MyClass
{
public string MyString { get; set; }
}
static void Main()
{
int i1 = 1; Test("i1", i1); // False
int i2 = 0; Test("i2", i2); // True
int? i3 = 2; Test("i3", i3); // False
int? i4 = null; Test("i4", i4); // True
Console.WriteLine();
string s1 = "hello"; Test("s1", s1); // False
string s2 = null; Test("s2", s2); // True
string s3 = string.Empty; Test("s3", s3); // True
string s4 = ""; Test("s4", s4); // True
Console.WriteLine();
MyClass mc1 = new MyClass(); Test("mc1", mc1); // False
MyClass mc2 = null; Test("mc2", mc2); // True
}
public static void Test<T>(string fieldName, T field)
{
Console.WriteLine(fieldName + ": " + IsNullOrEmpty(field));
}
// public static bool IsNullOrEmpty<T>(T value) ...
// public static bool IsNull<T>(T value) ...
}
I use:
public class MyClass<T>
{
private bool IsNull()
{
var nullable = Nullable.GetUnderlyingType(typeof(T)) != null;
return nullable ? EqualityComparer<T>.Default.Equals(Value, default(T)) : false;
}
}
Just a hacky answer and as a reminder for myself.
But I find this quite helpful for my project.
The reason I write it like this is that because I don't want default integer 0 being marked as null if the value is 0
private static int o;
public static void Main()
{
//output: IsNull = False -> IsDefault = True
Console.WriteLine( "IsNull = " + IsNull( o ) + " -> IsDefault = " + IsDefault(o));
}
public static bool IsNull<T>(T paramValue)
{
if( string.IsNullOrEmpty(paramValue + "" ))
return true;
return false;
}
public static bool IsDefault<T>(T val)
{
return EqualityComparer<T>.Default.Equals(val, default(T));
}
Don't know if this works with your requirements or not, but you could constrain T to be a Type that implements an interface such as IComparable and then use the ComparesTo() method from that interface (which IIRC supports/handles nulls) like this:
public void MyMethod<T>(T myArgument) where T : IComparable
...
if (0 == myArgument.ComparesTo(default(T)))
There are probably other interfaces that you could use as well IEquitable, etc.
#ilitirit:
public class Class<T> where T : IComparable
{
public T Value { get; set; }
public void MyMethod(T val)
{
if (Value == val)
return;
}
}
Operator '==' cannot be applied to operands of type 'T' and 'T'
I can't think of a way to do this without the explicit null test followed by invoking the Equals method or object.Equals as suggested above.
You can devise a solution using System.Comparison but really that's going to end up with way more lines of code and increase complexity substantially.
I think you were close.
if (myArgument.Equals(default(T)))
Now this compiles, but will fail if myArgument is null, which is part of what I'm testing for. I can add an explicit null check like this:
You just need to reverse the object on which the equals is being called for an elegant null-safe approach.
default(T).Equals(myArgument);

Null or default comparison of generic argument in C#

I have a generic method defined like this:
public void MyMethod<T>(T myArgument)
The first thing I want to do is check if the value of myArgument is the default value for that type, something like this:
if (myArgument == default(T))
But this doesn't compile because I haven't guaranteed that T will implement the == operator. So I switched the code to this:
if (myArgument.Equals(default(T)))
Now this compiles, but will fail if myArgument is null, which is part of what I'm testing for. I can add an explicit null check like this:
if (myArgument == null || myArgument.Equals(default(T)))
Now this feels redundant to me. ReSharper is even suggesting that I change the myArgument == null part into myArgument == default(T) which is where I started. Is there a better way to solve this problem?
I need to support both references types and value types.
To avoid boxing, the best way to compare generics for equality is with EqualityComparer<T>.Default. This respects IEquatable<T> (without boxing) as well as object.Equals, and handles all the Nullable<T> "lifted" nuances. Hence:
if(EqualityComparer<T>.Default.Equals(obj, default(T))) {
return obj;
}
This will match:
null for classes
null (empty) for Nullable<T>
zero/false/etc for other structs
How about this:
if (object.Equals(myArgument, default(T)))
{
//...
}
Using the static object.Equals() method avoids the need for you to do the null check yourself. Explicitly qualifying the call with object. probably isn't necessary depending on your context, but I normally prefix static calls with the type name just to make the code more soluble.
I was able to locate a Microsoft Connect article that discusses this issue in some detail:
Unfortunately, this behavior is by design and there is not an easy solution to enable use of with type parameters that may contain value types.
If the types are known to be reference types, the default overload of defined on object tests variables for reference equality, although a type may specify its own custom overload. The compiler determines which overload to use based on the static type of the variable (the determination is not polymorphic). Therefore, if you change your example to constrain the generic type parameter T to a non-sealed reference type (such as Exception), the compiler can determine the specific overload to use and the following code would compile:
public class Test<T> where T : Exception
If the types are known to be value types, performs specific value equality tests based on the exact types used. There is no good "default" comparison here since reference comparisons are not meaningful on value types and the compiler cannot know which specific value comparison to emit. The compiler could emit a call to ValueType.Equals(Object) but this method uses reflection and is quite inefficient compared to the specific value comparisons. Therefore, even if you were to specify a value-type constraint on T, there is nothing reasonable for the compiler to generate here:
public class Test<T> where T : struct
In the case you presented, where the compiler does not even know whether T is a value or reference type, there is similarly nothing to generate that would be valid for all possible types. A reference comparison would not be valid for value types and some sort of value comparison would be unexpected for reference types that do not overload.
Here is what you can do...
I have validated that both of these methods work for a generic comparison of reference and value types:
object.Equals(param, default(T))
or
EqualityComparer<T>.Default.Equals(param, default(T))
To do comparisons with the "==" operator you will need to use one of these methods:
If all cases of T derive from a known base class you can let the compiler know using generic type restrictions.
public void MyMethod<T>(T myArgument) where T : MyBase
The compiler then recognizes how to perform operations on MyBase and will not throw the "Operator '==' cannot be applied to operands of type 'T' and 'T'" error that you are seeing now.
Another option would be to restrict T to any type that implements IComparable.
public void MyMethod<T>(T myArgument) where T : IComparable
And then use the CompareTo method defined by the IComparable interface.
Try this:
if (EqualityComparer<T>.Default.Equals(myArgument, default(T)))
that should compile, and do what you want.
(Edited)
Marc Gravell has the best answer, but I wanted to post a simple code snippet I worked up to demonstrate it. Just run this in a simple C# console app:
public static class TypeHelper<T>
{
public static bool IsDefault(T val)
{
return EqualityComparer<T>.Default.Equals(obj,default(T));
}
}
static void Main(string[] args)
{
// value type
Console.WriteLine(TypeHelper<int>.IsDefault(1)); //False
Console.WriteLine(TypeHelper<int>.IsDefault(0)); // True
// reference type
Console.WriteLine(TypeHelper<string>.IsDefault("test")); //False
Console.WriteLine(TypeHelper<string>.IsDefault(null)); //True //True
Console.ReadKey();
}
One more thing: can someone with VS2008 try this as an extension method? I'm stuck with 2005 here and I'm curious to see if that would be allowed.
Edit: Here is how to get it working as an extension method:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// value type
Console.WriteLine(1.IsDefault());
Console.WriteLine(0.IsDefault());
// reference type
Console.WriteLine("test".IsDefault());
// null must be cast to a type
Console.WriteLine(((String)null).IsDefault());
}
}
// The type cannot be generic
public static class TypeHelper
{
// I made the method generic instead
public static bool IsDefault<T>(this T val)
{
return EqualityComparer<T>.Default.Equals(val, default(T));
}
}
To handle all types of T, including where T is a primitive type, you'll need to compile in both methods of comparison:
T Get<T>(Func<T> createObject)
{
T obj = createObject();
if (obj == null || obj.Equals(default(T)))
return obj;
// .. do a bunch of stuff
return obj;
}
Extension method based on accepted answer.
public static bool IsDefault<T>(this T inObj)
{
return EqualityComparer<T>.Default.Equals(inObj, default);
}
Usage:
private bool SomeMethod(){
var tValue = GetMyObject<MyObjectType>();
if (tValue == null || tValue.IsDefault()) return false;
}
Alternate with null to simplify:
public static bool IsNullOrDefault<T>(this T inObj)
{
if (inObj == null) return true;
return EqualityComparer<T>.Default.Equals(inObj, default);
}
Usage:
private bool SomeMethod(){
var tValue = GetMyObject<MyObjectType>();
if (tValue.IsNullOrDefault()) return false;
}
There is going to be a problem here -
If you're going to allow this to work for any type, default(T) will always be null for reference types, and 0 (or struct full of 0) for value types.
This is probably not the behavior you're after, though. If you want this to work in a generic way, you probably need to use reflection to check the type of T, and handle value types different than reference types.
Alternatively, you could put an interface constraint on this, and the interface could provide a way to check against the default of the class/struct.
I think you probably need to split this logic into two parts and check for null first.
public static bool IsNullOrEmpty<T>(T value)
{
if (IsNull(value))
{
return true;
}
if (value is string)
{
return string.IsNullOrEmpty(value as string);
}
return value.Equals(default(T));
}
public static bool IsNull<T>(T value)
{
if (value is ValueType)
{
return false;
}
return null == (object)value;
}
In the IsNull method, we're relying on the fact that ValueType objects can't be null by definition so if value happens to be a class which derives from ValueType, we already know it's not null. On the other hand, if it's not a value type then we can just compare value cast to an object against null. We could avoid the check against ValueType by going straight to a cast to object, but that would mean that a value type would get boxed which is something we probably want to avoid since it implies that a new object is created on the heap.
In the IsNullOrEmpty method, we're checking for the special case of a string. For all other types, we're comparing the value (which already know is not null) against it's default value which for all reference types is null and for value types is usually some form of zero (if they're integral).
Using these methods, the following code behaves as you might expect:
class Program
{
public class MyClass
{
public string MyString { get; set; }
}
static void Main()
{
int i1 = 1; Test("i1", i1); // False
int i2 = 0; Test("i2", i2); // True
int? i3 = 2; Test("i3", i3); // False
int? i4 = null; Test("i4", i4); // True
Console.WriteLine();
string s1 = "hello"; Test("s1", s1); // False
string s2 = null; Test("s2", s2); // True
string s3 = string.Empty; Test("s3", s3); // True
string s4 = ""; Test("s4", s4); // True
Console.WriteLine();
MyClass mc1 = new MyClass(); Test("mc1", mc1); // False
MyClass mc2 = null; Test("mc2", mc2); // True
}
public static void Test<T>(string fieldName, T field)
{
Console.WriteLine(fieldName + ": " + IsNullOrEmpty(field));
}
// public static bool IsNullOrEmpty<T>(T value) ...
// public static bool IsNull<T>(T value) ...
}
I use:
public class MyClass<T>
{
private bool IsNull()
{
var nullable = Nullable.GetUnderlyingType(typeof(T)) != null;
return nullable ? EqualityComparer<T>.Default.Equals(Value, default(T)) : false;
}
}
Just a hacky answer and as a reminder for myself.
But I find this quite helpful for my project.
The reason I write it like this is that because I don't want default integer 0 being marked as null if the value is 0
private static int o;
public static void Main()
{
//output: IsNull = False -> IsDefault = True
Console.WriteLine( "IsNull = " + IsNull( o ) + " -> IsDefault = " + IsDefault(o));
}
public static bool IsNull<T>(T paramValue)
{
if( string.IsNullOrEmpty(paramValue + "" ))
return true;
return false;
}
public static bool IsDefault<T>(T val)
{
return EqualityComparer<T>.Default.Equals(val, default(T));
}
Don't know if this works with your requirements or not, but you could constrain T to be a Type that implements an interface such as IComparable and then use the ComparesTo() method from that interface (which IIRC supports/handles nulls) like this:
public void MyMethod<T>(T myArgument) where T : IComparable
...
if (0 == myArgument.ComparesTo(default(T)))
There are probably other interfaces that you could use as well IEquitable, etc.
#ilitirit:
public class Class<T> where T : IComparable
{
public T Value { get; set; }
public void MyMethod(T val)
{
if (Value == val)
return;
}
}
Operator '==' cannot be applied to operands of type 'T' and 'T'
I can't think of a way to do this without the explicit null test followed by invoking the Equals method or object.Equals as suggested above.
You can devise a solution using System.Comparison but really that's going to end up with way more lines of code and increase complexity substantially.
I think you were close.
if (myArgument.Equals(default(T)))
Now this compiles, but will fail if myArgument is null, which is part of what I'm testing for. I can add an explicit null check like this:
You just need to reverse the object on which the equals is being called for an elegant null-safe approach.
default(T).Equals(myArgument);

Categories