.NET reflection provided by ExpressionTrees. An alternative for reflection by extensions methods based on Expressions. This extensions allow create new instances without activator, set or get property values of unknown types and unknown properties, check equality with unknown type default value, call object methods and extension methods avoiding Invoke, and couple of non-expression extensions for as/is.
Full table available here: Benchmark resuts
New()
average faster then Activator
in 10 times, short info:
Method | Mean | Allocated |
---|---|---|
New | 30.655 ns | 72 B |
New (params) | 72.224 ns | 144 B |
Activator | 558.993 ns | 568 B |
GetPropValue()
average faster then PropertyInfo
in 2-5 times
Method | Mean | Allocated |
---|---|---|
Reflection | 103.186 ns | 24 B |
GetPropValue | 17.372 ns | 24 B |
SetPropValue()
average faster then PropertyInfo
in 2-5 times
Method | Mean | Allocated |
---|---|---|
Reflection | 118.99 ns | 24 B |
SetPropValue | 32.04 ns | 128 B |
New
get/set
default
is
as
call
net6.0+
netcoreapp3.1+
netstandard2.1+
net46+
Alternative for Activator.CreateInstance
provided by Expression.New
lambda cached by System.Type
into Delegate
.
object New(this object obj)
object New<T1, T2, ..., T16>(this object obj, T1 arg1,T2 arg2,..., T16 arg16)
object New(this object obj, params object[] args)
Getting List<T>
as IList
of Type
in runtime:
public static IList IList(this Type type)
{
return typeof(List<>).MakeGenericType(type)
.New()
.As<IList>();
}
Create and apply migration from migration type:
void RunMigration(Type migrationType)
{
var migration = migrationType.New().As<CodegenMigration>();
...
migration.Apply();
...
}
There is three methods for getting value from property based on Expression.PropertyOrField
:
object GetPropValue(this object obj, string propName)
TValue GetPropValue<TValue>(this object obj, string propName)
TValue GetPropValue<THost, TValue>(this THost obj, string propName)
Example of getting 'Id' value of uknown type:
var rowAccess = new RowAccess
{
Type = typeof(T),
IdValue = entity.GetPropValue("Id") // value can be long/string/Guid or something
};
There is only one method for setting value:
void SetPropValue(this object obj, string propName, object propValue)
But it can works different based on passed value:
- Usually (for
nullable
too) its just set value by lambda - For
enum
values it wraps action withEnum.Parse(value.ToString())
- If you pass wrong
type
for value, it will try convert byConvert.ChangeType
Example method of setting 'Id' value when you don't know type:
public static void SetId<TId>(this object obj, TId value)
=> obj.SetPropValue("Id", value);
public static void SetId(this object obj, object value)
=> obj.SetPropValue("Id", value);
Example of create IList and bind to object:
var type = obj.GetType();
var prop = type.GetProperty('Users');
var list = prop.IList();
obj.SetPropValue(prop.Name, list);
Alternative of default
for System.Type
in runtime based on Expression.Default()
with cache available by DefaultExtensions.Default(this Type type)
:
var type = obj.GetType();
var prop = type.GetProperty('Id');
var value = prop.GetValue(obj);
if(value.Equals(type.Default())
Console.WriteLine("Property is not initialized!");
As<T>(this object)
As<T>(this object, bool throw)
As<T>(this object, T default)
Is<T>(this object)
Is<T>(this object, out T match)
IsNot<T>(this object)
IsNot<T>(this object, out T match)
Is<T>(this Type)
Is<T>(this Type, out T match)
IsNot<T>(this Type)
IsNot<T>(this Type, out T match)
IsSimple
IsPrimitive
IsNumericType
IsDateType
IsNullable
IsNullablePrimitive
IsAnonymousType
IsICollection
IsDbCollection
'String' checks differenlty
IsEmpty
IsNotEmpty
Extensions for call methods by ExpressionTrees
Available methods for 'call':
obj.Call("Equalls",null);
dbCtx.CallGeneric("Set",new Type[]{ typeof(User) });
typeof(Console).CallStatic("WriteLine", "log");
list.CallExtension("ToList",typeof("EnumerableExtensions");
Overloads for call static generic
, call generic extension
, Call with T result
etc included.
Example of using Call
chain with LiteDatabase
: gettin anonymous class from store in runtime:
var liteDbCollectionQueryable = LiteDb
.CallGeneric("GetCollection", new Type[] { type })
.Call("FindAll")
.CallGenericExtension("ToList", typeof(Enumerable), new[] { type })
.CallGenericExtension("AsQueryable", typeof(Queryable), new[] { type });
var filtered = SelectionModelConverter
.CallGeneric("Convert", new Type[] { type }, selection, liteDbCollectionQueryable)
.GetPropertyExprRaw("Result");
var (filteredPropertyConverted, anonymousType) = SelectionModelConverter
.CallGeneric<(IQueryable, Type)>("ConvertProperties", new Type[] { type }, selection.Properties, filtered); // returns IQueryable and anonymousType
var materialize = filteredPropertyConverted.CallGenericExtension("ToList", typeof(Enumerable), new[] { anonymousType });
Extensions for getting Type
s and instances of Type
from Assembly
and assembly names loaded from AppDomain.Current.BasePath
.
That's not speed-up alternative. You can check it by run some benchmarks. That's alternative
of System.Reflection.
With .NET6 all techniques of 'speed-up' by ExpressionTrees
and even source generators
is pointless because of reflection improvements described here: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/#reflection
But still it can be useful alternative for full .NET Framework with 'old' runtime.