Issue
From https://source.dot.net/#System.Private.CoreLib/Hashtable.cs,475:
public virtual bool ContainsKey(object key!!)
It looks like two null-forgiving operators. Is there a document about it?
Solution
This is a null-parameter check syntax which was going to be introduced in C# 11. This proposal has since been rolled back following community feedback.
The proposal is here, and the PR doing a first roll-out to the runtime is here.
The syntax:
public void Foo(string bar!!)
{
}
Is roughly equivalent to:
public void Foo(string bar)
{
if (bar is null)
{
throw new ArgumentNullException(nameof(bar));
}
}
... although the actual implementation uses a throw helper, something like:
public void Foo(string bar)
{
<PrivateImplementationDetails>.ThrowIfNull(bar, "bar");
}
[CompilerGenerated]
internal sealed class <PrivateImplementationDetails>
{
internal static void Throw(string paramName)
{
throw new ArgumentNullException(paramName);
}
internal static void ThrowIfNull(object argument, string paramName)
{
if (argument == null)
{
Throw(paramName);
}
}
}
Methods containing throw
statements are less likely to be inlined by the JIT, so using a throw helper makes it more likely that your method can be inlined, which might remove the null-check altogether! See on SharpLab.
Note that the use of !!
is an implementation detail of your method: it just causes the compiler to insert code which you could have written yourself anyway. This means that moving from throw
to !!
(or vice versa) is not a breaking change.
There are a couple of places where !!
will get the compiler to generate code which you can't (easily) write by hand, however.
One place where !!
is particularly useful is in records with primary constructors. For example:
public record Person(string Name!!, int Age);
In order to correctly null-check the Name
parameter in previous versions of C#, you have to write this out longhand:
public record Person
{
public string Name { get; init; }
public int Age { get; init; }
public Person(string name, int age)
{
if (name is null)
throw new ArgumentNullException(nameof(name));
(Name, Age) = (name, age);
}
public void Deconstruct(out string name, out int age) =>
(name, age) = (Name, Age);
}
Another place where !!
does something which you can't write yourself is in chained constructor calls:
public class C
{
public C(int i) { }
public C(string s!!) : this(s.Length) { }
}
This null-checks s
before accessing s.Length
, something like this (which isn't valid C#):
public C(string s)
{
if (s is null)
throw new ArgumentNullException(nameof(s));
C(s.Length);
}
Another interesting aspect is that the null-checks are inserted before field assignments in constructors. For example:
public class C
{
private readonly ExpensiveObject e = new ExpensiveObject();
public C(string s!!) { }
}
Is compiled as:
public class C
{
private readonly ExpensiveObject e;
public C(string s)
{
if (s is null)
throw new ArgumentNullException(nameof(s));
e = new ExpensiveObject();
}
}
That is, the null-check happens before the instantiation of ExpensiveObject
. See on SharpLab.
Answered By - canton7 Answer Checked By - Willingham (PHPFixing Volunteer)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.