Is there a built-in ExpectNonNull? - c#

In my code, when something is declared as nullable, but I expect it to be non-null at run-time, I can do something like this:
someObject.ExpectNonNull().SomeMember
The ExpectNonNull is implemented like this:
[return: NotNull]
public static T ExpectNonNull<T>(
[NotNull] this T? arg,
[CallerArgumentExpression(nameof(arg))] string arg_name = ""
)
where T : class
{
if (arg is null)
throw new ArgumentNullException(
arg_name,
$"The argument `{arg_name}` with type {typeof(T).Name} is expected to be non-null, but is null."
);
return arg;
}
(There is also a version for value types.)
Is there anything similar built-into the .NET itself?

As of .NET 6, you can use ArgumentNullException.ThrowIfNull for at least some of what you're after:
public void SomeMethod(string text)
{
ArgumentNullException.ThrowIfNull(text);
// Use text here, confident that it's not null
}
It doesn't allow the kind of fluent approach you're using, but in some cases it's all you need.

Related

C# 8.0 Nullable, How to indicate method checks for null [duplicate]

C# 8 introduced nullable reference types, which is a very cool feature. Now if you expect to get nullable values you have to write so-called guards:
object? value = null;
if (value is null)
{
throw new ArgumentNullException();
}
…
These can be a bit repetitive. What I am wondering is if it is possible to avoid writing this type of code for every variable, but instead have a guard-type static void function that throws exception if value is null or just returns if value is not null. Or is this too hard for compiler to infer? Especially if it's external library/package?
There are a few things you can do.
You can use [DoesNotReturnIf(...)] in your guard method, to indicate that it throws if a particular condition is true or false, for example:
public static class Ensure
{
public static void True([DoesNotReturnIf(false)] bool condition)
{
if (!condition)
{
throw new Exception("!!!");
}
}
}
Then:
public void TestMethod(object? o)
{
Ensure.True(o != null);
Console.WriteLine(o.ToString()); // No warning
}
This works because:
[DoesNotReturnIf(bool)]: Placed on a bool parameter. Code after the call is unreachable if the parameter has the specified bool value
Alternatively, you can declare a guard method like this:
public static class Ensure
{
public static void NotNull([NotNull] object? o)
{
if (o is null)
{
throw new Exception("!!!");
}
}
}
And use it like this:
public void TestMethod(object? o)
{
Ensure.NotNull(o);
Console.WriteLine(o.ToString()); // No warning
}
This works because:
[NotNull]: For outputs (ref/out parameters, return values), the output will not be null, even if the type allows it. For inputs (by-value/in parameters) the value passed is known not to be null when we return.
SharpLab with examples
Of course, the real question is why you want to do this. If you don't expect value to be null, then declare it as object?, rather than object -- that's the point of having NRTs.
There is a Guard Clauses library by Steve Ardalis that I think can help you with this situation.
You can do things like:
Guard.Against.Null (throws if input is null)
Guard.Against.NullOrEmpty (throws if string or array input is null or empty)
Guard.Against.NullOrWhiteSpace (throws if string input is null, empty or whitespace)
Guard.Against.OutOfRange (throws if integer/DateTime/enum input is outside a provided range)
Guard.Against.OutOfSQLDateRange (throws if DateTime input is outside the valid range of SQL Server DateTime values)
Guard.Against.Zero (throws if number input is zero)
In this blog post Jason Roberts made a quick explanation of the library too.
There is another Guard Class in the Microsoft.Toolkit.Diagnostics namespace but probably is not viable in all the use cases that will depend if wanna add that dependency to the project or not.

Implicit conversion from null

Looking Zoran Horvats courses at PluralSight, I'm currently implementing a Maybe type, a bit like Zoran has on its GitHub account: https://github.com/zoran-horvat/option
Generally, a Maybe is a wrapper around objects, which are either set or have a null value, avoiding null reference exceptions.
To make the code a bit shorter, I would like to use implicit conversion to map the values / nulls to their corresponding maybe types. Here an example of my code:
public void Hook(Maybe<Action<Keys>> onKeyDown, Maybe<Action<Keys>> onKeyUp)
{
_keyDownCallback = onKeyDown;
_keyUpCallback = onKeyUp;
_hookService.Hook(HookType.KeyBoardLowLevel, OnHookReceived);
}
As you can see, you can hook and pass two optional callbacks, one for keyDown and one for keyUp. I would like to pass code like this:
nativeKeyboardHookService.Hook(new Action<Keys>(OnNativeKeyDown), null);
The implicit conversion on the Maybe is currently implemented like this:
public static implicit operator Maybe<T>(T value)
{
return ToMaybe(value);
}
public static implicit operator T(Maybe<T> maybe)
{
return ToT(maybe);
}
public static Maybe<T> ToMaybe(T value)
{
if (value == null)
{
return new None<T>();
}
return new Some<T>(value);
}
public static T ToT(Maybe<T> maybe)
{
return maybe.Evaluate(
value => value,
() => default(T));
}
My question: It works fine, if I pass an actual object, mapping it to an Maybe, but if I pass NULL, I still get a NULL object, not a None object. Am I doing here something wrong or is it just not possible? I didn't find any further information regarding such a conversion.
When you pass null to Hook() that's literally all you are doing because your implicit casts aren't being invoked at all. That's because null is a valid value for a reference type, and thus no need to cast.
You can't change Maybe to a struct if you want to keep Some and None because then these would have to be structs too, which means you run into the issue that you can't inherit structs.
You can't implement a common IMaybe<T> interface either because interfaces can't be used with casts.
What I recommend is keep your behavior as is, but don't use null. Instead of passing null, pass something else like Maybe<T>.None:
class Maybe<T>
{
public static Maybe<T> None { get; } = new None<T>();
}
void Hook(..., Maybe<T>.None) { ... }
Or None<T>.Instance:
class None<T>
{
public static None<T> Instance{ get; } = new None<T>();
}
void Hook(..., None<T>.Instance) { ... }
This has the advantage of being more readable and explicit.
Your Maybe<T> is still a reference type, so null is a valid value for it:
Maybe<string> foo = null;
If you want to prevent that, you will need to make it a value type, for example something like this:
public struct Maybe<T>
{
public T Value { get; }
public bool IsEmpty => Value == null;
public Maybe(T value)
{
Value = value;
}
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>(value);
}
}
Then you can pass null to a method expecting a Maybe<T> and it will properly construct an empty Maybe<T> object.
But note that it being a value type, this now means that it is copied on every method call, so it has a different behavior to a reference type implementation.
In the end, you cannot really implement this nicely in C# simply because there is the null reference in the language. It’s only with C# 8’s nullable reference types that you will be able to prevent nulls altogether.

CA1004 and exception/error handling assistance

CA1004: Generic methods should provide type parameter
public static void IfNullAndNullsAreIllegalThenThrow<T>(object value)
{
if (value == null && !(default(T) == null))
throw new ArgumentException("Nulls are not allowed for this object.");
}
I found this method online, which is quite useful to be honest. But, it violates the CA1004 rule. I'm not sure if there is a better way to design the method and not violate the rules.
Sample usage:
public class SomeClass<T>
{
public void SomeMethod(object obj)
{
// Ensure the actual object is not null if it shouldn't be.
ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(obj);
// ...
}
}
This looks like a helper method which is only used internally. Make it internal instead of public and the warning should go away.
CA1004 is warning you that the generic type parameter cannot be inferred from the method's signature. Basically, it means you can only call it like this:
Something obj = GetSomething();
IfNullAndNullsAreIllegalThenThrow<Something>(obj);
On the other hand, if you redefine the method so its argument is of type T, then the generic type parameter can be inferred from the object passed:
public static void IfNullAndNullsAreIllegalThenThrow<T>(T value)
{
if (value == null && !(default(T) == null))
throw new ArgumentException("Nulls are not allowed for this object.");
}
So you can simply write:
Something obj = GetSomething();
IfNullAndNullsAreIllegalThenThrow(obj);

Detecting a Nullable Type via reflection

Surprisingly the following code fails the Assert:
int? wtf = 0;
Assert.IsType<Nullable<int>>(wtf);
So just out curiosity, how can you determine if a given instance is a Nullable<> object or not?
Well firstly, Nullable<T> is a struct, so there isn't an object as such. You can't call GetType(), as that will box the value (at which point you either get null and thus an exception, or a boxed non-nullable value and therefore not the type you want).
(Boxing is what's messing up your assertion here - I would assume that IsType accepts object.)
You can use type inference though to get the type of the variable as a type parameter:
public bool IsNullable<T>(T value)
{
return Nullable.GetUnderlyingType(typeof(T)) != null;
}
That's not a huge amount of use when you know the exact type at compile-time as in your example, but it's useful for generics. (There are alternative ways of implementing it, of course.)
What's your real life situation? I assume it's not an assertion like this, given that you know the answer to this one at compile time.
I like the #jon-skeet answer but it only works if you know the type you are testing against. In our world we are using reflection to open up objects and test values against regex expressions.
simplifying the extension to work for any type worked better for us.
public static bool IsNullable(this Type type)
{
return Nullable.GetUnderlyingType(type) != null;
}
generics are life's blood but sometimes... :)
int? i = 0;
var type = TypedReference.GetTargetType(__makeref(i));
var isNullable = type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(Nullable<>);
What namespace is Assert in?
The following returns true as you would expect:
int? wtf = 0;
if (typeof(Nullable<int>).IsInstanceOfType(wtf))
{
// Do things
}
Although its worth noting that typeof(Nullable<int>).IsInstanceOfType(42) also returns true - this is because this method accepts an object and so gets boxed as a Nullable<int>.
Here is what I came up with, as everything else seemed to fail - at least on Portable Class Library / DotNet Core with >= C# 6
Basically you extend generic Type Object and Nullable<T> and use the fact that the static extension method that matches the underlying type is going to be invoked and takes precedence over the generic T extension-method.
public static partial class ObjectExtension
{
public static bool IsNullable<T>(this T self)
{
return false;
}
}
and one for Nullable<T>
public static partial class NullableExtension
{
public static bool IsNullable<T>(this Nullable<T> self) where T : struct
{
return true;
}
}
Using Reflection and type.IsGeneric and type.GetGenericParameters() did not work on my current set of .NET Runtimes.
It works fo me, hope for you too.
public static bool IsNullable(this Type type)
{
return Nullable.GetUnderlyingType(type) != null;
}
Based on Vladimir answer:
public static class GenericExtensions
{
public static bool IsNullable<T>(this T item) =>
TypedReference.GetTargetType(__makeref(item)).FullName.Contains("System.Nullable");
}
Usage:
int? nullableInt = 42;
bool nIntIsNullable = nullableInt.IsNullable();
Duration: <2ms on average machine.
Remarks:
Important, this API is not CLS-compliant.
Contains("System.Nullable") can be more specific.
This works for me to check wether a type is Nullable or not..
type.Assembly.FullName.StartsWith("System") && type.Name.Contains("Nullable");
Only this way worked in my case using .net core 7
MyClass mclass = new MyClass();
PropertyInfo[] properties = mclass.GetType().GetProperties();
foreach (PropertyInfo propertyInfo in properties)
{
bool nullable = propertyInfo.GetMethod is null ? false : new NullabilityInfoContext().Create(propertyInfo.GetMethod.ReturnParameter).ReadState == NullabilityState.Nullable;
if (nullable)
{
//some script to do
}
}

Multiple generic methods with identical names and arguments, but different results and constraints

I'm currently rewriting parts of a custom RPC mechanism (which cannot be replaced by something else, so don't suggest that ;-) ). The arguments of a call are collected in a custom collection that uses a dictionary internally. There is a method T Get<T>(string) to retrieve a named argument. For optional arguments, I wanted to add a TryGet<T>(string) method that returns the argument or null if it doesn't exist, so that the calling code can provide a default value using the null coalescing operator. Of course, for a value type this doesn't work, but I could use T? instead, which is what I want.
So what I have is this:
public class Arguments
{
// lots of other code here
public T TryGet<T>(string argumentName) where T : class
{
// look up and return value or null if not found
}
public T? TryGet<T>(string argumentName) where T : struct
{
// look up and return value or null if not found
}
}
With that, I'd like to be able to do the following:
return new SomeObject(
args.TryGet<string>("Name") ?? "NoName",
args.TryGet<int>("Index") ?? 1
);
Since the constraints are mutually exclusive, the compiler should be able to produce the correct code (it's always possible to infer the call from the generic type given at the call site). The compiler complains that the type already defines a member called "TryGet" with the same parameter types.
Is there any way to make something like this work without giving the two methods different names?
Constraints are not part of the signature. thus the answer to your question is no.
The way classes in the .NET Framework handle this scenario is TryGetValue with an out parameter. The return value is an indicator of whether the get was successful, where the out parameter contains the value requested (on success) or a suitable default value (on failure).
This pattern makes the implementation very simple for reference and value types. You would only need a single method to handle both scenarios.
For an example of this pattern, see Dictionary<TKey,TValue>.TryGetValue.
The reason this doesn't work is because you cannot have two methods with the same name and same argument types (the return type is not taken into account for method overloading). Instead you could define a single method without the generic constraint which will work for both value and reference types:
public T TryGet<T>(string argumentName)
{
if (!_internalDictionary.ContainsKey(argumentName))
{
return default(T);
}
return (T)_internalDictionary[argumentName];
}
An alternative solution could be this one:
public class Arguments {
public T Get<T>(string argumentName,T defaultValue) {
// look up and return value or defaultValue if not found
}
}
return new SomeObject(
args.Get<string>("Name","NoName"),
args.Get<int>("Index",1)
);
In that particular case you would not even have to specify the generic type, as it could be inferred by the default parameter:
return new SomeObject(
args.Get("Name","NoName"),
args.Get("Index",1)
);
Although it does not work directly due to identical argument types, You can do that by adding optional defaultValue parameter which defaults to null:
public class Arguments
{
// lots of other code here
public T? TryGet<T>(string argumentName, T? defaultValue = null) where T : class
{
// look up and return value or null if not found
}
public T? TryGet<T>(string argumentName, T? defaultValue = null) where T : struct
{
// look up and return value or null if not found
}
}
The reason this one works is that second argument type is different for both contraints (In the the method with class constraint it is simply T, and in the method with struct constraint it is Nullbale<T>).
Following code works as you would expect:
var args = new Arguments();
var stringValue = args.TryGet<string>("Name") ?? "NoName";
var intValue = args.TryGet<int>("Index") ?? 1;

Categories

Resources