Is there any way to implement the interface ISetter
so that I do not need boxing when assigning the value? A direct cast (T)value
is obviously not possible (compiler error). Type T
can be a value or a class type. The interface ISetter
must not be generic itself as it should be used as value type in a common dictionary for different types T
.
public interface ISetter
{
void Set<T>(T value);
}
public class Prop<T> : ISetter
{
public T Value;
//will be called always matching T1 == T
public void Set<T1>(T1 value)
{
if (typeof(T1) != typeof(T)) throw new ArgumentException();
Value = (T)(object)value; //is there any way to avoid boxing
}
}
CodePudding user response:
To my knowledge it actually should not box the passed value. Every combination of T
and T1
where both are value types should be compiled separately so compiler has ability to optimize parts of the code resulting in the removal of the "intermediate" cast to object
.
Next benchmark (using BenchmarkDotNet
) shows no allocations on my machine (TargetFramework - netcoreapp3.1 with latest SDK installed):
[SimpleJob(RunStrategy.Monitoring)]
[MemoryDiagnoser]
public class BenchBoxedNonBoxedGeneric
{
private static Prop<int> GenericProp = new();
private static Prop NonGenericProp = new();
private const int Iterations = 1000_000_000;
[Benchmark]
public int NonGeneric()
{
for (int i = 0; i < Iterations; i )
{
NonGenericProp.Set(i);
}
return NonGenericProp.Value;
}
[Benchmark]
public int Generic()
{
for (int i = 0; i < Iterations; i )
{
GenericProp.Set(i);
}
return GenericProp.Value;
}
}
public class Prop<T>
{
public T Value;
public void Set<T1>(T1 value)
{
Value = (T)(object)value;
}
}
public class Prop
{
public int Value;
public void Set(int value)
{
Value = value;
}
}
Method | Mean | Error | StdDev | Allocated |
---|---|---|---|---|
NonGeneric | 405.2 ms | 26.76 ms | 17.70 ms | - |
Generic | 383.2 ms | 21.37 ms | 14.14 ms | - |
The decompilation to JIT Asm with sharplab.io seems to prove it too.