The duck typing library allows us to get and set data from fields and properties and call methods from an object without having the type definition at compile time. This is done by creating a proxy type to an object instance using a proxy definition at runtime.
The goal of the library is to have an unify code to access unknown types as fastest and with the minimum allocations as possible.
Given the following scenario, where we want to access the data from an anonymous class instance in another method, a code example to do that would be:
public class Program
{
public static void Main()
{
// We create an anonymous object
var anonymousObject = new { Name = ".NET Core", Version = "3.1" };
Process(anonymousObject);
}
public static void Process(object obj)
{
// First, we create a proxy instance using IDuckAnonymous type
// as a proxy definition and the obj instance as the target.
// Now the proxyInstance implements IDuckAnonymous type and all
// getters to access the anonymous object internals were generated
// automatically for us.
// We use the `As` extension method to call the duck typing proxy creator.
var proxyInstance = obj.As<IDuckAnonymous>();
// Here we can access the internal properties
Console.WriteLine($"Name: {proxyInstance.Name}");
Console.WriteLine($"Version: {proxyInstance.Version}");
}
public interface IDuckAnonymous
{
string Name { get; }
string Version { get; }
}
}
For this particular case the generated proxy type by the Duck Type library will look like this:
public readonly struct IDuckAnonymous___<>f__AnonymousType0`2[System.String,System.String] : IDuckAnonymous, IDuckType
{
private readonly <>f__AnonymousType0`2[System.String,System.String] _currentInstance;
// *** IDuckType implementation
public object Instance => _currentInstance;
public Type Type => typeof(<>f__AnonymousType0`2[System.String,System.String]);
// *** IDuckAnonymous implementation
public string Name => _currentInstance.Name;
public string Version => _currentInstance.Version;
}
Depending on the proxy definition type, the library will create different proxy types as shown in the following table:
Proxy Definition Type | Resulting Proxy Type |
---|---|
Interface | Struct implementing the interface |
Abstract class | Class inherits and overriding the abstract class |
Class with virtual members | Class inherits and overriding the virtual class |
Also, all resulting proxy types implements the IDuckType
interface to expose
the target instance and type. The interface has the following definition:
public interface IDuckType
{
/// <summary>
/// Gets instance
/// </summary>
object Instance { get; }
/// <summary>
/// Gets instance Type
/// </summary>
Type Type { get; }
}
To control the bindings between the proxy definition and the target type, we can
make use of the DuckAttribute
attribute type defined as:
/// <summary>
/// Duck attribute
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)]
public class DuckAttribute : Attribute
{
/// <summary>
/// Gets or sets the underlying type member name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets duck kind
/// </summary>
public DuckKind Kind { get; set; } = DuckKind.Property;
/// <summary>
/// Gets or sets the generic parameter type names definition for a generic method call (required when calling generic methods and instance type is non public)
/// </summary>
public string[] GenericParameterTypeNames { get; set; }
/// <summary>
/// Gets or sets the parameter type names of the target method (optional / used to disambiguation)
/// </summary>
public string[] ParameterTypeNames { get; set; }
}
/// <summary>
/// Duck attribute where the underlying member is a field
/// </summary>
public class DuckFieldAttribute : DuckAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="DuckFieldAttribute"/> class.
/// </summary>
public DuckFieldAttribute()
{
Kind = DuckKind.Field;
}
}
/// <summary>
/// Duck kind
/// </summary>
public enum DuckKind
{
/// <summary>
/// Property
/// </summary>
Property,
/// <summary>
/// Field
/// </summary>
Field
}
This attribute can be used in Properties
or Methods
in the proxy definition
and is used by the library to select the members we want to access.
The following example shows multiple uses of the DuckAttribute
:
public interface IMyProxy
{
// ***
// *** Field binding
// ***
[DuckField(Name = "_sampleStaticField")]
string MyStaticField { get; }
[DuckField(Name = "_normalField")]
int NormalFieldWithGetterAndSetter { get; set; }
// ***
// *** Property binding (by default the Kind is Property so we can ignore it)
// ***
[Duck(Name = "Instance")]
string MyStaticInstanceProperty { get; }
// If we don´t use the duck attribute it will try to find a property with the same name in the target type
string MyProperty { get; set; }
// If the original property returns a type that we don´t have at compile time,
// we can pass a proxy definition and that original value will be wrapped with the proxy
// as well (DuckType chaining).
IObscureObject CustomData { get; }
// Indexers are supported as well
string this[string index] { get; set; }
// ***
// *** Method binding
// ***
// support overloads of a method
void Add(string name, int obj);
void Add(string name, string obj = "none");
// In order to resolve an ambiguity of a call we can specify the parameters we want to match.
[Duck(ParameterTypeNames = new string[] { "System.String", "MyNamespace.MyType, MyAssembly" })]
void Add(string name, object obj);
// We can also use the name to map to another method name.
[Duck(Name = "InternalGetReference")]
void GetReference(ref int value);
// The Proxy definition can support generics only if the instance type
// is *Public*
Tuple<T1, T2> Wrap<T1, T2>(T1 a, T2 b);
// If we need to call a generic method from a non public type
// we can use the DuckAttribute as well:
[Duck(Name = "Wrap", GenericParameterTypeNames = new[] { "System.Int32", "System.String" })]
Tuple<int, string> WrapIntString(int a, string b);
// If the original call returns or uses a parameter that we don´t have at compile time,
// we can pass a proxy definition and that original value will be wrapped with the proxy
// as well (DuckType chaining).
bool TryGetPrivateObscure(out IObscureObject obj);
}
In order to support all accessor modifiers for: instance types, parameters
and return value types, the Duck Type library applies some tricks
to avoid
the visibility checks. This is done automatically when the library is creating
the proxy type. In summary the following logic is applied depending on each case:
Target Type AM | Target Member Type | Target Member AM | Access Method |
---|---|---|---|
Public | Field | Public | Direct |
Public | Field | Private, Protected, Internal | through DynamicMethod |
Public | Property | Public | Direct |
Public | Property | Private, Protected, Internal | Direct using function pointers (Calli opcode) |
Public | Method | Public | Direct |
Public | Method | Private, Protected, Internal | Direct using function pointers (Calli opcode) |
Private, Protected, Internal | Field | Public | through DynamicMethod delegate |
Private, Protected, Internal | Field | Private, Protected, Internal | through DynamicMethod delegate |
Private, Protected, Internal | Property | Public | through DynamicMethod delegate |
Private, Protected, Internal | Property | Private, Protected, Internal | through DynamicMethod delegate |
Private, Protected, Internal | Method | Public | through DynamicMethod delegate |
Private, Protected, Internal | Method | Private, Protected, Internal | through DynamicMethod delegate |
Calling generics methods are supported by the library, but due
the use of DynamicMethod
in some scenarios we can´t define it as a normal
generic method (Please note if we know that the target type is Public,
then we can declare a normal generic method).
For those scenarios were we can´t use the generic definition, the alternative is to specify the generic parameters to be match in the duck attribute of the proxy definition.
Consider this public target type:
public class PublicType
{
public Tuple<T1, T2> Wrap<T1, T2>(T1 a, T2 b)
{
return Tuple.Create<T1, T2>(default, default);
}
}
Because the type is public we can define a Generic proxy method:
public interface IMyProxy
{
Tuple<T1, T2> Wrap<T1, T2>(T1 a, T2 b);
}
Consider this private target type:
private class PrivateType
{
public Tuple<T1, T2> Wrap<T1, T2>(T1 a, T2 b)
{
return Tuple.Create<T1, T2>(default, default);
}
}
Because the type is non public we have to define each generic arguments we want to use in different calls.
public interface IMyProxy
{
// We have to define all kind of call we want to make to the target instance
[Duck(Name = "Wrap", GenericParameterTypeNames = new[] { "System.Int32", "System.String" })]
Tuple<int, string> WrapIntString(int a, string b);
[Duck(Name = "Wrap", GenericParameterTypeNames = new[] { "System.Int32", "System.Int32" })]
Tuple<int, int> WrapIntInt(int a, int b);
}
Duck chaining enables the possibility to interact with properties or methods returning or using non public type parameters to be wrapped with a new duck type proxy, so we can access the internals of those objects.
Consider the following types:
public class MyHandler
{
public string Name { get; set; }
internal MyHandlerConfiguration Configuration { get; }
}
internal class MyHandlerConfiguration
{
public int MaxConnections { get; set; }
}
We can write the following proxy definitions to get access to all the data:
public interface IProxyMyHandler
{
string Name { get; set; }
IProxyMyHandlerConfiguration Configuration { get; }
}
public interface IProxyMyHandlerConfiguration
{
int MaxConnections { get; set; }
}
In this example the non public instance of MyHandlerConfiguration
when calling
the Configuration
property is going to be wrapped with
a IProxyMyHandlerConfiguration
instance automatically. That allow us to access
the internal data of that non public type.
Several benchmark tests were run for multiple cases to keep track of the time execution and heap allocations of the library, these are the results:
The proxy
column indicates the target type access modifier.
Tests with blank
proxy are the direct access of the value through an interface
without creating any duck type proxy.
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.508 (2004/?/20H1)
Intel Core i7-1068NG7 CPU 2.30GHz, 1 CPU, 2 logical and 2 physical cores
.NET Core SDK=3.1.402
[Host] : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT
Job-LJAVIR : .NET Framework 4.8 (4.8.4220.0), X64 RyuJIT
Job-OHOUFK : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT
Method | Runtime | Categories | proxy | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|---|---|
GetPublicStaticField | .NET 4.7.2 | Static Getter | Internal | 3.207 ns | 0.0623 ns | 0.0553 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicStaticField | .NET Core 3.1 | Static Getter | Internal | 4.052 ns | 0.0748 ns | 0.0700 ns | 1.27 | 0.03 | - | - | - | - |
GetInternalStaticField | .NET 4.7.2 | Static Getter | Internal | 3.233 ns | 0.0774 ns | 0.0724 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalStaticField | .NET Core 3.1 | Static Getter | Internal | 3.987 ns | 0.0723 ns | 0.0677 ns | 1.23 | 0.04 | - | - | - | - |
GetProtectedStaticField | .NET 4.7.2 | Static Getter | Internal | 3.229 ns | 0.0607 ns | 0.0567 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedStaticField | .NET Core 3.1 | Static Getter | Internal | 4.004 ns | 0.1010 ns | 0.0895 ns | 1.24 | 0.04 | - | - | - | - |
GetPrivateStaticField | .NET 4.7.2 | Static Getter | Internal | 3.209 ns | 0.0288 ns | 0.0255 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateStaticField | .NET Core 3.1 | Static Getter | Internal | 4.274 ns | 0.0539 ns | 0.0421 ns | 1.33 | 0.02 | - | - | - | - |
SetPublicStaticField | .NET 4.7.2 | Static Setter | Internal | 2.941 ns | 0.0507 ns | 0.0423 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicStaticField | .NET Core 3.1 | Static Setter | Internal | 3.966 ns | 0.0788 ns | 0.0737 ns | 1.34 | 0.03 | - | - | - | - |
SetInternalStaticField | .NET 4.7.2 | Static Setter | Internal | 2.952 ns | 0.0671 ns | 0.0627 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalStaticField | .NET Core 3.1 | Static Setter | Internal | 3.973 ns | 0.0804 ns | 0.0752 ns | 1.35 | 0.04 | - | - | - | - |
SetProtectedStaticField | .NET 4.7.2 | Static Setter | Internal | 2.880 ns | 0.0472 ns | 0.0394 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedStaticField | .NET Core 3.1 | Static Setter | Internal | 3.872 ns | 0.0359 ns | 0.0300 ns | 1.34 | 0.02 | - | - | - | - |
SetPrivateStaticField | .NET 4.7.2 | Static Setter | Internal | 2.945 ns | 0.0625 ns | 0.0585 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateStaticField | .NET Core 3.1 | Static Setter | Internal | 3.966 ns | 0.0846 ns | 0.0791 ns | 1.35 | 0.04 | - | - | - | - |
GetPublicField | .NET 4.7.2 | Getter | Internal | 3.405 ns | 0.0599 ns | 0.0531 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicField | .NET Core 3.1 | Getter | Internal | 3.229 ns | 0.0571 ns | 0.0477 ns | 0.95 | 0.02 | - | - | - | - |
GetInternalField | .NET 4.7.2 | Getter | Internal | 3.400 ns | 0.0465 ns | 0.0389 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalField | .NET Core 3.1 | Getter | Internal | 3.549 ns | 0.0657 ns | 0.0615 ns | 1.05 | 0.02 | - | - | - | - |
GetProtectedField | .NET 4.7.2 | Getter | Internal | 3.384 ns | 0.0459 ns | 0.0383 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedField | .NET Core 3.1 | Getter | Internal | 3.278 ns | 0.0503 ns | 0.0392 ns | 0.97 | 0.02 | - | - | - | - |
GetPrivateField | .NET 4.7.2 | Getter | Internal | 3.425 ns | 0.0545 ns | 0.0510 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateField | .NET Core 3.1 | Getter | Internal | 3.282 ns | 0.0761 ns | 0.0675 ns | 0.96 | 0.02 | - | - | - | - |
SetPublicField | .NET 4.7.2 | Setter | Internal | 3.392 ns | 0.0649 ns | 0.0575 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicField | .NET Core 3.1 | Setter | Internal | 3.164 ns | 0.0509 ns | 0.0451 ns | 0.93 | 0.02 | - | - | - | - |
SetInternalField | .NET 4.7.2 | Setter | Internal | 3.422 ns | 0.0727 ns | 0.0680 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalField | .NET Core 3.1 | Setter | Internal | 3.166 ns | 0.0430 ns | 0.0359 ns | 0.92 | 0.02 | - | - | - | - |
SetProtectedField | .NET 4.7.2 | Setter | Internal | 3.384 ns | 0.0392 ns | 0.0306 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedField | .NET Core 3.1 | Setter | Internal | 3.180 ns | 0.0520 ns | 0.0486 ns | 0.94 | 0.02 | - | - | - | - |
SetPrivateField | .NET 4.7.2 | Setter | Internal | 3.392 ns | 0.0548 ns | 0.0485 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateField | .NET Core 3.1 | Setter | Internal | 3.155 ns | 0.0300 ns | 0.0234 ns | 0.93 | 0.02 | - | - | - | - |
GetPublicStaticField | .NET 4.7.2 | Static Getter | Private | 3.163 ns | 0.0390 ns | 0.0326 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicStaticField | .NET Core 3.1 | Static Getter | Private | 4.310 ns | 0.0717 ns | 0.0671 ns | 1.36 | 0.03 | - | - | - | - |
GetInternalStaticField | .NET 4.7.2 | Static Getter | Private | 3.220 ns | 0.0737 ns | 0.0689 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalStaticField | .NET Core 3.1 | Static Getter | Private | 3.999 ns | 0.0327 ns | 0.0255 ns | 1.24 | 0.03 | - | - | - | - |
GetProtectedStaticField | .NET 4.7.2 | Static Getter | Private | 3.198 ns | 0.0458 ns | 0.0383 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedStaticField | .NET Core 3.1 | Static Getter | Private | 4.082 ns | 0.0989 ns | 0.0925 ns | 1.27 | 0.02 | - | - | - | - |
GetPrivateStaticField | .NET 4.7.2 | Static Getter | Private | 3.185 ns | 0.0336 ns | 0.0280 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateStaticField | .NET Core 3.1 | Static Getter | Private | 3.993 ns | 0.0543 ns | 0.0453 ns | 1.25 | 0.02 | - | - | - | - |
SetPublicStaticField | .NET 4.7.2 | Static Setter | Private | 2.928 ns | 0.0462 ns | 0.0386 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicStaticField | .NET Core 3.1 | Static Setter | Private | 3.951 ns | 0.0857 ns | 0.0760 ns | 1.35 | 0.03 | - | - | - | - |
SetInternalStaticField | .NET 4.7.2 | Static Setter | Private | 3.006 ns | 0.0861 ns | 0.0805 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalStaticField | .NET Core 3.1 | Static Setter | Private | 3.986 ns | 0.0890 ns | 0.0833 ns | 1.33 | 0.04 | - | - | - | - |
SetProtectedStaticField | .NET 4.7.2 | Static Setter | Private | 2.916 ns | 0.0256 ns | 0.0214 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedStaticField | .NET Core 3.1 | Static Setter | Private | 3.937 ns | 0.0509 ns | 0.0425 ns | 1.35 | 0.02 | - | - | - | - |
SetPrivateStaticField | .NET 4.7.2 | Static Setter | Private | 3.003 ns | 0.0684 ns | 0.0639 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateStaticField | .NET Core 3.1 | Static Setter | Private | 3.951 ns | 0.0954 ns | 0.0846 ns | 1.32 | 0.03 | - | - | - | - |
GetPublicField | .NET 4.7.2 | Getter | Private | 3.408 ns | 0.0499 ns | 0.0417 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicField | .NET Core 3.1 | Getter | Private | 3.243 ns | 0.0506 ns | 0.0448 ns | 0.95 | 0.01 | - | - | - | - |
GetInternalField | .NET 4.7.2 | Getter | Private | 3.419 ns | 0.0360 ns | 0.0301 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalField | .NET Core 3.1 | Getter | Private | 3.276 ns | 0.0638 ns | 0.0565 ns | 0.96 | 0.02 | - | - | - | - |
GetProtectedField | .NET 4.7.2 | Getter | Private | 3.428 ns | 0.0688 ns | 0.0644 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedField | .NET Core 3.1 | Getter | Private | 3.260 ns | 0.0414 ns | 0.0367 ns | 0.95 | 0.02 | - | - | - | - |
GetPrivateField | .NET 4.7.2 | Getter | Private | 3.533 ns | 0.0972 ns | 0.1264 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateField | .NET Core 3.1 | Getter | Private | 3.275 ns | 0.0576 ns | 0.0511 ns | 0.92 | 0.04 | - | - | - | - |
SetPublicField | .NET 4.7.2 | Setter | Private | 3.437 ns | 0.0919 ns | 0.0903 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicField | .NET Core 3.1 | Setter | Private | 3.151 ns | 0.0520 ns | 0.0461 ns | 0.92 | 0.03 | - | - | - | - |
SetInternalField | .NET 4.7.2 | Setter | Private | 3.384 ns | 0.0533 ns | 0.0445 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalField | .NET Core 3.1 | Setter | Private | 3.156 ns | 0.0327 ns | 0.0273 ns | 0.93 | 0.02 | - | - | - | - |
SetProtectedField | .NET 4.7.2 | Setter | Private | 3.368 ns | 0.0396 ns | 0.0331 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedField | .NET Core 3.1 | Setter | Private | 2.953 ns | 0.0264 ns | 0.0221 ns | 0.88 | 0.01 | - | - | - | - |
SetPrivateField | .NET 4.7.2 | Setter | Private | 3.353 ns | 0.0471 ns | 0.0393 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateField | .NET Core 3.1 | Setter | Private | 3.160 ns | 0.0592 ns | 0.0525 ns | 0.94 | 0.02 | - | - | - | - |
GetPublicStaticField | .NET 4.7.2 | Static Getter | Public | 2.433 ns | 0.0616 ns | 0.0576 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicStaticField | .NET Core 3.1 | Static Getter | Public | 2.191 ns | 0.0497 ns | 0.0465 ns | 0.90 | 0.03 | - | - | - | - |
GetInternalStaticField | .NET 4.7.2 | Static Getter | Public | 3.189 ns | 0.0460 ns | 0.0384 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalStaticField | .NET Core 3.1 | Static Getter | Public | 3.964 ns | 0.0426 ns | 0.0332 ns | 1.24 | 0.02 | - | - | - | - |
GetProtectedStaticField | .NET 4.7.2 | Static Getter | Public | 3.201 ns | 0.0625 ns | 0.0522 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedStaticField | .NET Core 3.1 | Static Getter | Public | 3.998 ns | 0.0359 ns | 0.0300 ns | 1.25 | 0.02 | - | - | - | - |
GetPrivateStaticField | .NET 4.7.2 | Static Getter | Public | 3.220 ns | 0.0675 ns | 0.0631 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateStaticField | .NET Core 3.1 | Static Getter | Public | 4.068 ns | 0.0766 ns | 0.0717 ns | 1.26 | 0.04 | - | - | - | - |
SetPublicStaticField | .NET 4.7.2 | Static Setter | Public | 2.175 ns | 0.0506 ns | 0.0474 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicStaticField | .NET Core 3.1 | Static Setter | Public | 2.088 ns | 0.0281 ns | 0.0235 ns | 0.96 | 0.02 | - | - | - | - |
SetInternalStaticField | .NET 4.7.2 | Static Setter | Public | 2.972 ns | 0.0674 ns | 0.0598 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalStaticField | .NET Core 3.1 | Static Setter | Public | 3.966 ns | 0.0569 ns | 0.0504 ns | 1.34 | 0.03 | - | - | - | - |
SetProtectedStaticField | .NET 4.7.2 | Static Setter | Public | 2.936 ns | 0.0451 ns | 0.0376 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedStaticField | .NET Core 3.1 | Static Setter | Public | 3.923 ns | 0.0517 ns | 0.0432 ns | 1.34 | 0.02 | - | - | - | - |
SetPrivateStaticField | .NET 4.7.2 | Static Setter | Public | 2.957 ns | 0.0740 ns | 0.0656 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateStaticField | .NET Core 3.1 | Static Setter | Public | 3.933 ns | 0.0221 ns | 0.0172 ns | 1.33 | 0.03 | - | - | - | - |
GetPublicField | .NET 4.7.2 | Getter | Public | 2.326 ns | 0.0442 ns | 0.0369 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicField | .NET Core 3.1 | Getter | Public | 2.421 ns | 0.0646 ns | 0.0573 ns | 1.04 | 0.04 | - | - | - | - |
GetInternalField | .NET 4.7.2 | Getter | Public | 3.418 ns | 0.0579 ns | 0.0483 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalField | .NET Core 3.1 | Getter | Public | 3.251 ns | 0.0490 ns | 0.0409 ns | 0.95 | 0.02 | - | - | - | - |
GetProtectedField | .NET 4.7.2 | Getter | Public | 3.424 ns | 0.0471 ns | 0.0393 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedField | .NET Core 3.1 | Getter | Public | 3.232 ns | 0.0316 ns | 0.0264 ns | 0.94 | 0.01 | - | - | - | - |
GetPrivateField | .NET 4.7.2 | Getter | Public | 3.384 ns | 0.0300 ns | 0.0251 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateField | .NET Core 3.1 | Getter | Public | 3.338 ns | 0.0897 ns | 0.0839 ns | 0.98 | 0.03 | - | - | - | - |
SetPublicField | .NET 4.7.2 | Setter | Public | 2.078 ns | 0.0375 ns | 0.0333 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicField | .NET Core 3.1 | Setter | Public | 2.396 ns | 0.0510 ns | 0.0477 ns | 1.15 | 0.03 | - | - | - | - |
SetInternalField | .NET 4.7.2 | Setter | Public | 3.402 ns | 0.0424 ns | 0.0354 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalField | .NET Core 3.1 | Setter | Public | 3.146 ns | 0.0248 ns | 0.0207 ns | 0.92 | 0.01 | - | - | - | - |
SetProtectedField | .NET 4.7.2 | Setter | Public | 3.368 ns | 0.0443 ns | 0.0370 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedField | .NET Core 3.1 | Setter | Public | 3.167 ns | 0.0242 ns | 0.0189 ns | 0.94 | 0.01 | - | - | - | - |
SetPrivateField | .NET 4.7.2 | Setter | Public | 3.392 ns | 0.0563 ns | 0.0499 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateField | .NET Core 3.1 | Setter | Public | 3.171 ns | 0.0663 ns | 0.0621 ns | 0.94 | 0.02 | - | - | - | - |
The proxy
column indicates the target type access modifier.
Tests with blank
proxy are the direct access of the value through an interface
without creating any duck type proxy.
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.508 (2004/?/20H1)
Intel Core i7-1068NG7 CPU 2.30GHz, 1 CPU, 2 logical and 2 physical cores
.NET Core SDK=3.1.402
[Host] : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT
Job-QIOTKF : .NET Framework 4.8 (4.8.4220.0), X64 RyuJIT
Job-FEKUVA : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT
Method | Runtime | Categories | proxy | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|---|---|
GetPublicProperty | .NET 4.7.2 | Getter | 1.046 ns | 0.0457 ns | 0.0449 ns | 1.00 | 0.00 | - | - | - | - | |
GetPublicProperty | .NET Core 3.1 | Getter | 1.300 ns | 0.0329 ns | 0.0292 ns | 1.24 | 0.05 | - | - | - | - | |
SetPublicProperty | .NET 4.7.2 | Setter | 1.283 ns | 0.0225 ns | 0.0188 ns | 1.00 | 0.00 | - | - | - | - | |
SetPublicProperty | .NET Core 3.1 | Setter | 1.048 ns | 0.0262 ns | 0.0232 ns | 0.82 | 0.02 | - | - | - | - | |
GetIndexerProperty | .NET 4.7.2 | Indexer Getter | 1.027 ns | 0.0271 ns | 0.0240 ns | 1.00 | 0.00 | - | - | - | - | |
GetIndexerProperty | .NET Core 3.1 | Indexer Getter | 1.014 ns | 0.0435 ns | 0.0407 ns | 0.99 | 0.04 | - | - | - | - | |
SetIndexerProperty | .NET 4.7.2 | Indexer Setter | 1.303 ns | 0.0366 ns | 0.0324 ns | 1.00 | 0.00 | - | - | - | - | |
SetIndexerProperty | .NET Core 3.1 | Indexer Setter | 1.315 ns | 0.0383 ns | 0.0358 ns | 1.01 | 0.04 | - | - | - | - | |
GetPublicStaticProperty | .NET 4.7.2 | Static Getter | Internal | 2.849 ns | 0.0529 ns | 0.0442 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicStaticProperty | .NET Core 3.1 | Static Getter | Internal | 4.029 ns | 0.0897 ns | 0.0839 ns | 1.42 | 0.04 | - | - | - | - |
GetInternalStaticProperty | .NET 4.7.2 | Static Getter | Internal | 2.888 ns | 0.0606 ns | 0.0567 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalStaticProperty | .NET Core 3.1 | Static Getter | Internal | 4.263 ns | 0.0799 ns | 0.0708 ns | 1.48 | 0.04 | - | - | - | - |
GetProtectedStaticProperty | .NET 4.7.2 | Static Getter | Internal | 2.855 ns | 0.0372 ns | 0.0311 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedStaticProperty | .NET Core 3.1 | Static Getter | Internal | 4.277 ns | 0.0655 ns | 0.0612 ns | 1.50 | 0.02 | - | - | - | - |
GetPrivateStaticProperty | .NET 4.7.2 | Static Getter | Internal | 2.911 ns | 0.0726 ns | 0.0679 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateStaticProperty | .NET Core 3.1 | Static Getter | Internal | 4.030 ns | 0.0721 ns | 0.0675 ns | 1.39 | 0.04 | - | - | - | - |
SetPublicStaticProperty | .NET 4.7.2 | Static Setter | Internal | 3.221 ns | 0.0757 ns | 0.0632 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicStaticProperty | .NET Core 3.1 | Static Setter | Internal | 3.955 ns | 0.0575 ns | 0.0481 ns | 1.23 | 0.03 | - | - | - | - |
SetInternalStaticProperty | .NET 4.7.2 | Static Setter | Internal | 3.174 ns | 0.0334 ns | 0.0279 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalStaticProperty | .NET Core 3.1 | Static Setter | Internal | 4.212 ns | 0.0608 ns | 0.0507 ns | 1.33 | 0.01 | - | - | - | - |
SetProtectedStaticProperty | .NET 4.7.2 | Static Setter | Internal | 3.247 ns | 0.0846 ns | 0.1100 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedStaticProperty | .NET Core 3.1 | Static Setter | Internal | 4.190 ns | 0.0312 ns | 0.0243 ns | 1.27 | 0.05 | - | - | - | - |
SetPrivateStaticProperty | .NET 4.7.2 | Static Setter | Internal | 3.205 ns | 0.0714 ns | 0.0668 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateStaticProperty | .NET Core 3.1 | Static Setter | Internal | 4.249 ns | 0.0814 ns | 0.0721 ns | 1.32 | 0.03 | - | - | - | - |
GetPublicProperty | .NET 4.7.2 | Getter | Internal | 3.241 ns | 0.0758 ns | 0.0672 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicProperty | .NET Core 3.1 | Getter | Internal | 3.257 ns | 0.0730 ns | 0.0683 ns | 1.01 | 0.03 | - | - | - | - |
GetInternalProperty | .NET 4.7.2 | Getter | Internal | 3.239 ns | 0.0467 ns | 0.0390 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalProperty | .NET Core 3.1 | Getter | Internal | 3.256 ns | 0.0570 ns | 0.0476 ns | 1.01 | 0.02 | - | - | - | - |
GetProtectedProperty | .NET 4.7.2 | Getter | Internal | 3.290 ns | 0.0504 ns | 0.0447 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedProperty | .NET Core 3.1 | Getter | Internal | 3.350 ns | 0.0911 ns | 0.0935 ns | 1.02 | 0.03 | - | - | - | - |
GetPrivateProperty | .NET 4.7.2 | Getter | Internal | 3.259 ns | 0.0609 ns | 0.0540 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateProperty | .NET Core 3.1 | Getter | Internal | 3.243 ns | 0.0814 ns | 0.0722 ns | 1.00 | 0.03 | - | - | - | - |
SetPublicProperty | .NET 4.7.2 | Setter | Internal | 3.192 ns | 0.0749 ns | 0.0664 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicProperty | .NET Core 3.1 | Setter | Internal | 2.912 ns | 0.0481 ns | 0.0401 ns | 0.91 | 0.02 | - | - | - | - |
SetInternalProperty | .NET 4.7.2 | Setter | Internal | 3.211 ns | 0.0482 ns | 0.0427 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalProperty | .NET Core 3.1 | Setter | Internal | 2.914 ns | 0.0401 ns | 0.0335 ns | 0.91 | 0.02 | - | - | - | - |
SetProtectedProperty | .NET 4.7.2 | Setter | Internal | 3.176 ns | 0.0477 ns | 0.0399 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedProperty | .NET Core 3.1 | Setter | Internal | 2.890 ns | 0.0454 ns | 0.0379 ns | 0.91 | 0.02 | - | - | - | - |
SetPrivateProperty | .NET 4.7.2 | Setter | Internal | 3.170 ns | 0.0466 ns | 0.0413 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateProperty | .NET Core 3.1 | Setter | Internal | 2.948 ns | 0.0720 ns | 0.0674 ns | 0.93 | 0.03 | - | - | - | - |
GetIndexerProperty | .NET 4.7.2 | Indexer Getter | Internal | 3.274 ns | 0.0724 ns | 0.0642 ns | 1.00 | 0.00 | - | - | - | - |
GetIndexerProperty | .NET Core 3.1 | Indexer Getter | Internal | 3.282 ns | 0.0818 ns | 0.0765 ns | 1.00 | 0.01 | - | - | - | - |
SetIndexerProperty | .NET 4.7.2 | Indexer Setter | Internal | 3.087 ns | 0.0686 ns | 0.0674 ns | 1.00 | 0.00 | - | - | - | - |
SetIndexerProperty | .NET Core 3.1 | Indexer Setter | Internal | 3.184 ns | 0.0323 ns | 0.0252 ns | 1.03 | 0.03 | - | - | - | - |
GetPublicStaticProperty | .NET 4.7.2 | Static Getter | Private | 2.876 ns | 0.0561 ns | 0.0497 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicStaticProperty | .NET Core 3.1 | Static Getter | Private | 4.013 ns | 0.0914 ns | 0.0810 ns | 1.40 | 0.04 | - | - | - | - |
GetInternalStaticProperty | .NET 4.7.2 | Static Getter | Private | 2.851 ns | 0.0339 ns | 0.0283 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalStaticProperty | .NET Core 3.1 | Static Getter | Private | 4.019 ns | 0.0669 ns | 0.0559 ns | 1.41 | 0.02 | - | - | - | - |
GetProtectedStaticProperty | .NET 4.7.2 | Static Getter | Private | 2.838 ns | 0.0373 ns | 0.0311 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedStaticProperty | .NET Core 3.1 | Static Getter | Private | 4.035 ns | 0.0962 ns | 0.0900 ns | 1.42 | 0.03 | - | - | - | - |
GetPrivateStaticProperty | .NET 4.7.2 | Static Getter | Private | 2.918 ns | 0.0825 ns | 0.0731 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateStaticProperty | .NET Core 3.1 | Static Getter | Private | 4.134 ns | 0.0657 ns | 0.0614 ns | 1.42 | 0.03 | - | - | - | - |
SetPublicStaticProperty | .NET 4.7.2 | Static Setter | Private | 3.219 ns | 0.0472 ns | 0.0418 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicStaticProperty | .NET Core 3.1 | Static Setter | Private | 3.989 ns | 0.0664 ns | 0.0588 ns | 1.24 | 0.03 | - | - | - | - |
SetInternalStaticProperty | .NET 4.7.2 | Static Setter | Private | 3.271 ns | 0.0901 ns | 0.0843 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalStaticProperty | .NET Core 3.1 | Static Setter | Private | 4.304 ns | 0.0783 ns | 0.0694 ns | 1.32 | 0.04 | - | - | - | - |
SetProtectedStaticProperty | .NET 4.7.2 | Static Setter | Private | 3.241 ns | 0.0724 ns | 0.0677 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedStaticProperty | .NET Core 3.1 | Static Setter | Private | 4.180 ns | 0.0391 ns | 0.0305 ns | 1.29 | 0.03 | - | - | - | - |
SetPrivateStaticProperty | .NET 4.7.2 | Static Setter | Private | 3.161 ns | 0.0371 ns | 0.0310 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateStaticProperty | .NET Core 3.1 | Static Setter | Private | 4.186 ns | 0.0527 ns | 0.0412 ns | 1.33 | 0.01 | - | - | - | - |
GetPublicProperty | .NET 4.7.2 | Getter | Private | 3.245 ns | 0.0466 ns | 0.0389 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicProperty | .NET Core 3.1 | Getter | Private | 3.320 ns | 0.0889 ns | 0.0988 ns | 1.03 | 0.03 | - | - | - | - |
GetInternalProperty | .NET 4.7.2 | Getter | Private | 3.237 ns | 0.0475 ns | 0.0371 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalProperty | .NET Core 3.1 | Getter | Private | 3.301 ns | 0.0874 ns | 0.0972 ns | 1.01 | 0.03 | - | - | - | - |
GetProtectedProperty | .NET 4.7.2 | Getter | Private | 3.255 ns | 0.0309 ns | 0.0241 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedProperty | .NET Core 3.1 | Getter | Private | 3.295 ns | 0.0436 ns | 0.0340 ns | 1.01 | 0.01 | - | - | - | - |
GetPrivateProperty | .NET 4.7.2 | Getter | Private | 3.280 ns | 0.0656 ns | 0.0614 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateProperty | .NET Core 3.1 | Getter | Private | 3.309 ns | 0.0864 ns | 0.0766 ns | 1.01 | 0.03 | - | - | - | - |
SetPublicProperty | .NET 4.7.2 | Setter | Private | 3.205 ns | 0.0679 ns | 0.0635 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicProperty | .NET Core 3.1 | Setter | Private | 2.959 ns | 0.0682 ns | 0.0605 ns | 0.93 | 0.03 | - | - | - | - |
SetInternalProperty | .NET 4.7.2 | Setter | Private | 3.184 ns | 0.0479 ns | 0.0400 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalProperty | .NET Core 3.1 | Setter | Private | 2.943 ns | 0.0632 ns | 0.0592 ns | 0.92 | 0.03 | - | - | - | - |
SetProtectedProperty | .NET 4.7.2 | Setter | Private | 3.247 ns | 0.0927 ns | 0.1206 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedProperty | .NET Core 3.1 | Setter | Private | 2.934 ns | 0.0608 ns | 0.0539 ns | 0.89 | 0.04 | - | - | - | - |
SetPrivateProperty | .NET 4.7.2 | Setter | Private | 3.182 ns | 0.0711 ns | 0.0594 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateProperty | .NET Core 3.1 | Setter | Private | 2.938 ns | 0.0562 ns | 0.0499 ns | 0.92 | 0.02 | - | - | - | - |
GetIndexerProperty | .NET 4.7.2 | Indexer Getter | Private | 3.334 ns | 0.0826 ns | 0.0772 ns | 1.00 | 0.00 | - | - | - | - |
GetIndexerProperty | .NET Core 3.1 | Indexer Getter | Private | 3.523 ns | 0.0732 ns | 0.0611 ns | 1.05 | 0.02 | - | - | - | - |
SetIndexerProperty | .NET 4.7.2 | Indexer Setter | Private | 3.099 ns | 0.0479 ns | 0.0374 ns | 1.00 | 0.00 | - | - | - | - |
SetIndexerProperty | .NET Core 3.1 | Indexer Setter | Private | 3.167 ns | 0.0535 ns | 0.0447 ns | 1.02 | 0.02 | - | - | - | - |
GetPublicStaticProperty | .NET 4.7.2 | Static Getter | Public | 2.085 ns | 0.0345 ns | 0.0288 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicStaticProperty | .NET Core 3.1 | Static Getter | Public | 2.159 ns | 0.0326 ns | 0.0255 ns | 1.03 | 0.02 | - | - | - | - |
GetInternalStaticProperty | .NET 4.7.2 | Static Getter | Public | 2.969 ns | 0.0492 ns | 0.0384 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalStaticProperty | .NET Core 3.1 | Static Getter | Public | 3.197 ns | 0.0484 ns | 0.0404 ns | 1.08 | 0.02 | - | - | - | - |
GetProtectedStaticProperty | .NET 4.7.2 | Static Getter | Public | 2.935 ns | 0.0434 ns | 0.0363 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedStaticProperty | .NET Core 3.1 | Static Getter | Public | 3.234 ns | 0.0757 ns | 0.0632 ns | 1.10 | 0.03 | - | - | - | - |
GetPrivateStaticProperty | .NET 4.7.2 | Static Getter | Public | 3.003 ns | 0.0723 ns | 0.0676 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateStaticProperty | .NET Core 3.1 | Static Getter | Public | 3.024 ns | 0.0561 ns | 0.0525 ns | 1.01 | 0.03 | - | - | - | - |
SetPublicStaticProperty | .NET 4.7.2 | Static Setter | Public | 2.443 ns | 0.0405 ns | 0.0359 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicStaticProperty | .NET Core 3.1 | Static Setter | Public | 2.073 ns | 0.0400 ns | 0.0334 ns | 0.85 | 0.01 | - | - | - | - |
SetInternalStaticProperty | .NET 4.7.2 | Static Setter | Public | 3.109 ns | 0.0536 ns | 0.0448 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalStaticProperty | .NET Core 3.1 | Static Setter | Public | 3.174 ns | 0.0548 ns | 0.0486 ns | 1.02 | 0.02 | - | - | - | - |
SetProtectedStaticProperty | .NET 4.7.2 | Static Setter | Public | 3.120 ns | 0.0602 ns | 0.0503 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedStaticProperty | .NET Core 3.1 | Static Setter | Public | 3.194 ns | 0.0629 ns | 0.0557 ns | 1.02 | 0.02 | - | - | - | - |
SetPrivateStaticProperty | .NET 4.7.2 | Static Setter | Public | 3.161 ns | 0.0777 ns | 0.0727 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateStaticProperty | .NET Core 3.1 | Static Setter | Public | 3.194 ns | 0.0502 ns | 0.0419 ns | 1.01 | 0.03 | - | - | - | - |
GetPublicProperty | .NET 4.7.2 | Getter | Public | 2.214 ns | 0.0432 ns | 0.0383 ns | 1.00 | 0.00 | - | - | - | - |
GetPublicProperty | .NET Core 3.1 | Getter | Public | 2.215 ns | 0.0705 ns | 0.0692 ns | 1.00 | 0.04 | - | - | - | - |
GetInternalProperty | .NET 4.7.2 | Getter | Public | 2.869 ns | 0.0425 ns | 0.0355 ns | 1.00 | 0.00 | - | - | - | - |
GetInternalProperty | .NET Core 3.1 | Getter | Public | 3.079 ns | 0.0644 ns | 0.0602 ns | 1.07 | 0.03 | - | - | - | - |
GetProtectedProperty | .NET 4.7.2 | Getter | Public | 2.876 ns | 0.0450 ns | 0.0376 ns | 1.00 | 0.00 | - | - | - | - |
GetProtectedProperty | .NET Core 3.1 | Getter | Public | 3.033 ns | 0.0757 ns | 0.0671 ns | 1.06 | 0.03 | - | - | - | - |
GetPrivateProperty | .NET 4.7.2 | Getter | Public | 2.902 ns | 0.0455 ns | 0.0380 ns | 1.00 | 0.00 | - | - | - | - |
GetPrivateProperty | .NET Core 3.1 | Getter | Public | 3.011 ns | 0.0552 ns | 0.0461 ns | 1.04 | 0.02 | - | - | - | - |
SetPublicProperty | .NET 4.7.2 | Setter | Public | 2.377 ns | 0.0558 ns | 0.0522 ns | 1.00 | 0.00 | - | - | - | - |
SetPublicProperty | .NET Core 3.1 | Setter | Public | 2.102 ns | 0.0543 ns | 0.0508 ns | 0.88 | 0.02 | - | - | - | - |
SetInternalProperty | .NET 4.7.2 | Setter | Public | 3.138 ns | 0.0822 ns | 0.0808 ns | 1.00 | 0.00 | - | - | - | - |
SetInternalProperty | .NET Core 3.1 | Setter | Public | 2.898 ns | 0.0652 ns | 0.0578 ns | 0.92 | 0.03 | - | - | - | - |
SetProtectedProperty | .NET 4.7.2 | Setter | Public | 3.142 ns | 0.0820 ns | 0.0727 ns | 1.00 | 0.00 | - | - | - | - |
SetProtectedProperty | .NET Core 3.1 | Setter | Public | 2.857 ns | 0.0366 ns | 0.0305 ns | 0.91 | 0.02 | - | - | - | - |
SetPrivateProperty | .NET 4.7.2 | Setter | Public | 3.145 ns | 0.0640 ns | 0.0567 ns | 1.00 | 0.00 | - | - | - | - |
SetPrivateProperty | .NET Core 3.1 | Setter | Public | 2.865 ns | 0.0398 ns | 0.0332 ns | 0.91 | 0.02 | - | - | - | - |
GetIndexerProperty | .NET 4.7.2 | Indexer Getter | Public | 2.214 ns | 0.0256 ns | 0.0200 ns | 1.00 | 0.00 | - | - | - | - |
GetIndexerProperty | .NET Core 3.1 | Indexer Getter | Public | 2.229 ns | 0.0704 ns | 0.0811 ns | 1.01 | 0.04 | - | - | - | - |
SetIndexerProperty | .NET 4.7.2 | Indexer Setter | Public | 2.316 ns | 0.0323 ns | 0.0270 ns | 1.00 | 0.00 | - | - | - | - |
SetIndexerProperty | .NET Core 3.1 | Indexer Setter | Public | 2.360 ns | 0.0341 ns | 0.0302 ns | 1.02 | 0.01 | - | - | - | - |
The proxy
column indicates the target type access modifier.
Tests with blank
proxy are the direct access of the value through an interface
without creating any duck type proxy.
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.508 (2004/?/20H1)
Intel Core i7-1068NG7 CPU 2.30GHz, 1 CPU, 2 logical and 2 physical cores
.NET Core SDK=3.1.402
[Host] : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT
Job-CGLGGF : .NET Framework 4.8 (4.8.4220.0), X64 RyuJIT
Job-GEDFFR : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT
Method | Runtime | Categories | proxy | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|---|---|
PublicVoidMethod | .NET 4.7.2 | Void Method | 1.3457 ns | 0.0441 ns | 0.0391 ns | 1.00 | 0.00 | - | - | - | - | |
PublicVoidMethod | .NET Core 3.1 | Void Method | 1.3195 ns | 0.0300 ns | 0.0266 ns | 0.98 | 0.03 | - | - | - | - | |
PublicMethod | .NET 4.7.2 | Method | 0.7032 ns | 0.0647 ns | 0.0605 ns | 1.00 | 0.00 | - | - | - | - | |
PublicMethod | .NET Core 3.1 | Method | 1.0446 ns | 0.0595 ns | 0.0557 ns | 1.50 | 0.16 | - | - | - | - | |
PublicOutParameterMethod | .NET 4.7.2 | Out-Param Method | 2.2154 ns | 0.0305 ns | 0.0271 ns | 1.00 | 0.00 | - | - | - | - | |
PublicOutParameterMethod | .NET Core 3.1 | Out-Param Method | 2.2246 ns | 0.0649 ns | 0.0607 ns | 1.01 | 0.03 | - | - | - | - | |
PublicVoidMethod | .NET 4.7.2 | Void Method | Internal | 3.3373 ns | 0.0401 ns | 0.0335 ns | 1.00 | 0.00 | - | - | - | - |
PublicVoidMethod | .NET Core 3.1 | Void Method | Internal | 3.4379 ns | 0.0713 ns | 0.0632 ns | 1.03 | 0.02 | - | - | - | - |
PrivateVoidMethod | .NET 4.7.2 | Void Method | Internal | 3.3397 ns | 0.0408 ns | 0.0341 ns | 1.00 | 0.00 | - | - | - | - |
PrivateVoidMethod | .NET Core 3.1 | Void Method | Internal | 3.4176 ns | 0.0515 ns | 0.0456 ns | 1.02 | 0.02 | - | - | - | - |
PublicMethod | .NET 4.7.2 | Method | Internal | 3.1409 ns | 0.0730 ns | 0.0647 ns | 1.00 | 0.00 | - | - | - | - |
PublicMethod | .NET Core 3.1 | Method | Internal | 3.7283 ns | 0.0737 ns | 0.0689 ns | 1.19 | 0.04 | - | - | - | - |
PrivateMethod | .NET 4.7.2 | Method | Internal | 3.1578 ns | 0.1190 ns | 0.1113 ns | 1.00 | 0.00 | - | - | - | - |
PrivateMethod | .NET Core 3.1 | Method | Internal | 3.3137 ns | 0.1062 ns | 0.0942 ns | 1.05 | 0.05 | - | - | - | - |
PublicOutParameterMethod | .NET 4.7.2 | Out-Param Method | Internal | 4.2402 ns | 0.0436 ns | 0.0340 ns | 1.00 | 0.00 | - | - | - | - |
PublicOutParameterMethod | .NET Core 3.1 | Out-Param Method | Internal | 4.5497 ns | 0.1064 ns | 0.0995 ns | 1.07 | 0.02 | - | - | - | - |
PrivateOutParameterMethod | .NET 4.7.2 | Out-Param Method | Internal | 4.2640 ns | 0.0368 ns | 0.0307 ns | 1.00 | 0.00 | - | - | - | - |
PrivateOutParameterMethod | .NET Core 3.1 | Out-Param Method | Internal | 4.5834 ns | 0.0937 ns | 0.1003 ns | 1.07 | 0.02 | - | - | - | - |
PublicVoidMethod | .NET 4.7.2 | Void Method | Private | 3.3801 ns | 0.0650 ns | 0.0576 ns | 1.00 | 0.00 | - | - | - | - |
PublicVoidMethod | .NET Core 3.1 | Void Method | Private | 3.4223 ns | 0.0696 ns | 0.0651 ns | 1.01 | 0.02 | - | - | - | - |
PrivateVoidMethod | .NET 4.7.2 | Void Method | Private | 3.3691 ns | 0.0569 ns | 0.0532 ns | 1.00 | 0.00 | - | - | - | - |
PrivateVoidMethod | .NET Core 3.1 | Void Method | Private | 3.3862 ns | 0.0657 ns | 0.0549 ns | 1.00 | 0.02 | - | - | - | - |
PublicMethod | .NET 4.7.2 | Method | Private | 3.0958 ns | 0.0645 ns | 0.0538 ns | 1.00 | 0.00 | - | - | - | - |
PublicMethod | .NET Core 3.1 | Method | Private | 3.6469 ns | 0.0410 ns | 0.0320 ns | 1.18 | 0.02 | - | - | - | - |
PrivateMethod | .NET 4.7.2 | Method | Private | 3.1414 ns | 0.0910 ns | 0.0852 ns | 1.00 | 0.00 | - | - | - | - |
PrivateMethod | .NET Core 3.1 | Method | Private | 3.3168 ns | 0.0972 ns | 0.0910 ns | 1.06 | 0.04 | - | - | - | - |
PublicOutParameterMethod | .NET 4.7.2 | Out-Param Method | Private | 4.2712 ns | 0.0519 ns | 0.0485 ns | 1.00 | 0.00 | - | - | - | - |
PublicOutParameterMethod | .NET Core 3.1 | Out-Param Method | Private | 4.4966 ns | 0.0507 ns | 0.0423 ns | 1.05 | 0.01 | - | - | - | - |
PrivateOutParameterMethod | .NET 4.7.2 | Out-Param Method | Private | 4.2237 ns | 0.0299 ns | 0.0234 ns | 1.00 | 0.00 | - | - | - | - |
PrivateOutParameterMethod | .NET Core 3.1 | Out-Param Method | Private | 4.5464 ns | 0.0818 ns | 0.0765 ns | 1.08 | 0.02 | - | - | - | - |
PublicVoidMethod | .NET 4.7.2 | Void Method | Public | 2.3150 ns | 0.0717 ns | 0.0767 ns | 1.00 | 0.00 | - | - | - | - |
PublicVoidMethod | .NET Core 3.1 | Void Method | Public | 2.4284 ns | 0.0395 ns | 0.0330 ns | 1.04 | 0.04 | - | - | - | - |
PrivateVoidMethod | .NET 4.7.2 | Void Method | Public | 2.9836 ns | 0.0834 ns | 0.0780 ns | 1.00 | 0.00 | - | - | - | - |
PrivateVoidMethod | .NET Core 3.1 | Void Method | Public | 3.1669 ns | 0.0851 ns | 0.0796 ns | 1.06 | 0.04 | - | - | - | - |
PublicMethod | .NET 4.7.2 | Method | Public | 1.7100 ns | 0.0664 ns | 0.0555 ns | 1.00 | 0.00 | - | - | - | - |
PublicMethod | .NET Core 3.1 | Method | Public | 2.1847 ns | 0.0759 ns | 0.0710 ns | 1.27 | 0.06 | - | - | - | - |
PrivateMethod | .NET 4.7.2 | Method | Public | 2.7196 ns | 0.1125 ns | 0.1053 ns | 1.00 | 0.00 | - | - | - | - |
PrivateMethod | .NET Core 3.1 | Method | Public | 2.4933 ns | 0.0783 ns | 0.0694 ns | 0.91 | 0.04 | - | - | - | - |
PublicOutParameterMethod | .NET 4.7.2 | Out-Param Method | Public | 2.9139 ns | 0.0538 ns | 0.0420 ns | 1.00 | 0.00 | - | - | - | - |
PublicOutParameterMethod | .NET Core 3.1 | Out-Param Method | Public | 3.1691 ns | 0.0546 ns | 0.0456 ns | 1.09 | 0.02 | - | - | - | - |
PrivateOutParameterMethod | .NET 4.7.2 | Out-Param Method | Public | 3.7001 ns | 0.0380 ns | 0.0318 ns | 1.00 | 0.00 | - | - | - | - |
PrivateOutParameterMethod | .NET Core 3.1 | Out-Param Method | Public | 3.9932 ns | 0.0553 ns | 0.0490 ns | 1.08 | 0.01 | - | - | - | - |