Reputation: 6806
I'm trying to find out wether Roslyn does support the following use-case:
I use a Guard
class for parameter validation
public void Foo(string bar)
{
Guard.NotNull(bar, nameof(bar));
// ....
}
Nothing fancy and the nameof(...)
expression even makes it refactoring-friendly. But it's still redundant. And nothing prevents me from doing something like this
public void Foo(string bar, string baz)
{
Guard.NotNull(bar, nameof(baz));
// ...
}
So if there were a way to avoid the nameof(...)
part completely that would be nice.
So I would like to enable Roslyn to do something similar to the [CallerMemberNameattribute]
, just for parameters.
public static class Guard
{
public static void NotNull(object param, [CallerParameterName]string paramName = "")
{
if (param == null)
{
throw new ArgumentNullException(paramName);
}
}
}
public void Foo(string bar)
{
Guard.NotNull(bar);
// ...
}
I don't want to change the code before compiling it (like a refactoring or code fix would). I don't want to see values for arguments annotated with the CallerParameterNameAttribute
in source code at all (exactly like I don't see them for CallerMemberName
or CallerLineNumber
etc.).
I want Roslyn to inject the names of the parameters at compiletime for me.
Both Source Generators and Code Generators can only add source code (not change it) and compile it. The regular Roslyn analyzers also change source code afaik.
Any idea if that part of Roslyn is publicly accessible and where I can find information on how to use it?
Update: I don't want to use an IL Weaver like PostSharp. Just teach Roslyn to treat my custom attribute like one of the System.Runtime.CompilerService
attributes.
Upvotes: 2
Views: 507
Reputation: 2360
I'm not using Roslyn for this answer but this maybe help
You can use reflection to get the member expression and throw the exception with the argument name:
public class Guard
{
public static void NotNull<T1>(Expression<Func<T1>> expression)
{
Func<T1> check = expression.Compile();
if (check() is null)
{
var member = expression.Body;
if (member is MemberExpression)
{
string name = ((MemberExpression)member).Member.Name;
throw new ArgumentNullException(name);
}
}
}
}
public class Program
{
public static void Foo(string myParameterName)
{
Guard.NotNull(() => myParameterName);
}
public static void Main(string[] args)
{
Foo(null);
return;
}
}
So, the expcetion will be throw the original parameter name: myPropertyName
I also tested in Release configuration (-c Release) to check if myParameterName was not changed to another compiled name but not fully investigated this.
The downside of this code is the usage of a lambda expression, reflection (to not mention call of .Compile) to simple check if a variable is null with can be impactful in performance.
Upvotes: 1
Reputation: 1593
You can do with Fody: https://github.com/Fody/Fody . E.g. https://github.com/Fody/NullGuard .
Upvotes: 0