I make a microservices Buffet . In this I consider having email as a service . When the DevOps wants email, he can choose between various plugins ( simple smtp email, gmail, exchange, others). Those plugins can have various properties – that must be edited by the primary administrator of the microservice. The properties can be discovered at runtime ( via Reflection ) o r at build time ( via Roslyn Source Code Generators – RSCG ).
But – we should see what is faster, right ? And the feeling is that RSCG is always faster – but it is , really ? Let’s see…
So = let’s make a test with https://github.com/dotnet/BenchmarkDotNet . You can have the source code by going to https://github.com/ignatandrei/AOP_With_Roslyn/tree/master/AOPMethods .
First , the class that is tested
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public partial class EmailSmtpClientMS
{
public EmailSmtpClientMS()
{
Port = 25;
}
public string Name { get ; set ; }
public string Type
{
get
{
return this .GetType().Name;
}
}
public string Host { get ; set ; }
public int Port { get ; set ; }
public string Description
{
get
{
return $ "{Type} {Host}:{Port}" ;
}
}
}
|
Second, with AOPMethods I generate the read properties values – properties that you can read – via a dictionary and via a switch. This can be achieved simply :
1 2 | [AutoMethods(template = TemplateMethod.CustomTemplateFile, CustomTemplateFileName = "ClassToDictionary.txt" )]
public partial class EmailSmtpClientMS
|
And this will be generated by RSCG for the switch
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | protected object GetValueProperty( string propName)
{
switch (propName)
{
case "Name" :
return this .Name;
case "Type" :
return this .Type;
case "Host" :
return this .Host;
case "Port" :
return this .Port;
case "Description" :
return this .Description;
default :
throw new ArgumentException( "cannot find property " + propName);
}
}
|
and for the dictionary
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | private IDictionary< string , PropertyHelper> MyProperties()
{
var data = new Dictionary< string , PropertyHelper>();
PropertyHelper ph;
ph = new PropertyHelper();
ph.Name = "Name" ;
ph.Type = "string" ;
ph.CanRead = ! false ;
ph.CanWrite = ! false ;
ph.Value = this .Name;
data.Add( "Name" , ph);
ph = new PropertyHelper();
ph.Name = "Type" ;
ph.Type = "string" ;
ph.CanRead = ! false ;
ph.CanWrite = ! true ;
ph.Value = this .Type;
data.Add( "Type" , ph);
ph = new PropertyHelper();
ph.Name = "Host" ;
ph.Type = "string" ;
ph.CanRead = ! false ;
ph.CanWrite = ! false ;
ph.Value = this .Host;
data.Add( "Host" , ph);
ph = new PropertyHelper();
ph.Name = "Port" ;
ph.Type = "int" ;
ph.CanRead = ! false ;
ph.CanWrite = ! false ;
ph.Value = this .Port;
data.Add( "Port" , ph);
ph = new PropertyHelper();
ph.Name = "Description" ;
ph.Type = "string" ;
ph.CanRead = ! false ;
ph.CanWrite = ! true ;
ph.Value = this .Description;
data.Add( "Description" , ph);
return data;
}
|
The spec for benchmark are :
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1052 (21H1/May2021Update)
Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores
.NET SDK=5.0.301
[Host] : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT
DefaultJob : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT
Third, I benchmark obtaining one single property – the Host – via the 3 methods:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | public partial class EmailSmtpClientMSOneProperty: EmailSmtpClientMS
{
[Benchmark]
public string GetHostReflection()
{
return this .GetType().GetProperty( "Host" ).GetValue( this ).ToString();
}
[Benchmark]
public string GetHostViaDictionary()
{
return this .ReadMyProperties()[ "Host" ].ToString();
}
[Benchmark]
public string GetHostViaSwitch()
{
return this .GetValueProperty( "Host" ).ToString();
}
}
|
And in Program.cs
1 2 3 4 5 | BenchmarkRunner.Run<EmailSmtpClientMSOneProperty>(
ManualConfig
.Create(DefaultConfig.Instance)
.WithOption(ConfigOptions.DisableOptimizationsValidator, true )
);
|
( of course, I have added on the class
//[SimpleJob(RuntimeMoniker.Net50)]
//[ShortRunJob(RuntimeMoniker.Net50)]
//[DryJob(RuntimeMoniker.Net50)]
[Orderer(SummaryOrderPolicy.FastestToSlowest, MethodOrderPolicy.Declared)]
//[SimpleJob(RuntimeMoniker.NetCoreApp31)]
[RPlotExporter]
[CsvMeasurementsExporter]
[MemoryDiagnoser]
[HtmlExporter]
[MarkdownExporterAttribute.GitHub]
)
The results are in ns –so, the less/smaller , that means better results.
The results are here in HTML form :
Method |
Mean |
Error |
StdDev |
Median |
Gen 0 |
Gen 1 |
Gen 2 |
Allocated |
GetHostViaSwitch |
18.07 ns |
0.434 ns |
0.549 ns |
18.02 ns |
– |
– |
– |
– |
GetHostReflection |
144.13 ns |
2.582 ns |
5.501 ns |
142.13 ns |
– |
– |
– |
– |
GetHostViaDictionary |
451.59 ns |
12.363 ns |
33.635 ns |
441.72 ns |
0.3057 |
– |
– |
640 B |
The graphic may be more interesting:

Surprised ? The RSCG Switch Property is indeed the fastest one – but the Reflection is faster than RSCG Property Dictionary ( or , at least , for my implementation).
However, I realized that in real life , I will retrieve all properties in a Dictionary to be edited . So all implementations should occur the penalty of creating a Dictionary . Time for next benchmark . This time , the code is
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | [Benchmark]
public IDictionary< string , object > GetHostReflection()
{
var props = this .GetType()
.GetProperties()
.Where(it=> it.CanWrite)
.ToDictionary(it => it.Name, it=>it.GetValue( this ));
;
return props;
}
[Benchmark]
public IDictionary< string , object > GetHostViaDictionary()
{
var props = this .ReadMyProperties();
return props;
}
[Benchmark]
public IDictionary< string , object > GetHostViaSwitch()
{
var props = ReadProperties
.ToDictionary(it => it, it => GetValueProperty(it));
return props;
}
|
and the results are:
Method |
Mean |
Error |
StdDev |
Median |
Gen 0 |
Gen 1 |
Gen 2 |
Allocated |
GetHostViaDictionary |
462.1 ns |
14.70 ns |
40.97 ns |
453.6 ns |
0.3052 |
– |
– |
640 B |
GetHostViaSwitch |
479.5 ns |
7.34 ns |
7.54 ns |
479.9 ns |
0.2708 |
– |
– |
568 B |
GetHostReflection |
973.0 ns |
78.35 ns |
231.01 ns |
911.5 ns |
0.1984 |
– |
– |
416 B |
Now the graphic will help:

Interesting , right ?
Reflection = as normal – is the slowest one. But the difference between RSCG switch and RSCG Dictionary is not too much…
Conclusion 1: the feeling was right at the end. But – the first result was deceiving
Conclusion 2: Creating a dictionary is more time consuming than a simple reflection for one property retrieved
Conclusion 3: I do prefer RSCG Dictionary vs RSCG switch – less work for me as a programmer and similar time results.
Conclusion 4: do not over engineer if you do not feel the need . For just one property, Reflection is better….
Conclusion 5: This is not final. I should also write the values of the properties . Maybe next time a new benchmark….