How to use Reflection in order to avoid value boxing? - c#

I am exploring Blazor's QuickGrid source code and found one interesting spot here.
On the 45th line Steve Sanderson left a TODO with a potentially better alternative solution.
I could not resist my curiosity and decided to give it a try and benchmark the solution afterwards. But, unfortunately, my knowledge in Reflection is really poor.
Could anybody help me to understand how the ideas described in the Steve's comment could be achieved?
Thanks
UPD-1: Code snippet of what I have atm
if( typeof(TProp).GetInterface(nameof(IFormattable)) != null ) {
Type result = typeof(Func<,>).MakeGenericType(typeof(TGridItem), typeof(TProp));
_cellTextFunc = item => FormatValue(compiledPropertyExpression);
}
// TODO: Consider using reflection to avoid having to box every value just to call IFormattable.ToString
// For example, define a method "string Format<U>(Func<TGridItem, U> property) where U: IFormattable", and
// then construct the closed type here with U=TProp when we know TProp implements IFormattable
private string FormatValue<U>( Func<TGridItem, U> property ) where U : IFormattable
{
return null;
}

I suspect what Steve meant was to define a method such as:
private string? FormatItem<U>(TGridItem item, Func<TGridItem, U> property) where U: IFormattable
{
return property(item)?.ToString(Format, null);
}
(I had to add item to make it make sense: perhaps I'm missing something).
We also need to define a spearate version for nullables, since Nullable<T> can't satisfy the constraint IFormattable<T>, even if T does implement IFormattable<T>.
private string? FormatItemNullable<U>(TGridItem item, Func<TGridItem, U?> property) where U : struct, IFormattable
{
return property(item)?.ToString(Format, null);
}
Then once you know U, you can create a delegate which invokes this method:
var formatItemMethodInfo = typeof(C<TGridItem, TProp>).GetMethod("FormatItem", BindingFlags.NonPublic | BindingFlags.Instance)!;
var formatItemNullableMethodInfo = typeof(C<TGridItem, TProp>).GetMethod("FormatItemNullable", BindingFlags.NonPublic | BindingFlags.Instance)!;
var formatter = (Func<TGridItem, Func<TGridItem, TProp>, string>)Delegate.CreateDelegate(
typeof(Func<TGridItem, Func<TGridItem, TProp>, string>),
this,
nullableUnderlyingTypeOrNull == null
? formatItemMethodInfo.MakeGenericMethod(typeof(TProp))
: formatItemNullableMethodInfo.MakeGenericMethod(nullableUnderlyingTypeOrNull));
And finally use this when constructing _cellTextFunc:
_cellTextFunc = item => formatter(item, compiledPropertyExpression);
This has a little bit of upfront cost (creating the delegate), but invoking it is cheap, and means that you don't have to box item to IFormattable if it's a value type.
See on SharpLab.
If I were to suggest a better solution, since we're already using compiled expressions with Property, I'd extend that and compile the ToString call into the expression. Something like:
var toStringMethod = typeof(IFormattable).GetMethod("ToString")!;
var propVar = Expression.Variable(typeof(TProp), "prop");
// What to call String(...) on. We need to call prop.GetValueOrDefault().ToString(...)
// if prop is a nullable value type
Expression toStringCallTarget = nullableUnderlyingTypeOrNull == null
? propVar
: Expression.Call(
propVar,
typeof(TProp).GetMethod("GetValueOrDefault", Type.EmptyTypes)!);
// The ToString(...) call itself
var toStringCall = Expression.Call(
toStringCallTarget,
toStringMethod,
Expression.Property(Expression.Constant(this), "Format"),
Expression.Constant(null, typeof(IFormatProvider)));
// If prop is nullable (class or nullable struct), then we need to do
// prop == null ? null : prop[.GetValueOrDefault()].ToString(...),
// otherwise just call prop.ToString(...)
Expression conditionalCall = default(TProp) == null
? Expression.Condition(
Expression.Equal(propVar, Expression.Constant(null, typeof(TProp))),
Expression.Constant(null, typeof(string)),
toStringCall)
: toStringCall;
var block = Expression.Block(new[] { propVar },
Expression.Assign(propVar, Property.Body),
conditionalCall);
_cellTextFunc = Expression.Lambda<Func<TGridItem, string?>>(block, Property.Parameters[0]).Compile();
See it on SharpLab.
Just for fun, I put together a benchmark of the original approaches, Steve's proposed approach, and mine and MarcGravell's proposed solutions:
namespace MyBenchmarks
{
[MemoryDiagnoser]
public class Benchmark
{
private readonly GridItem gridItem = new();
private readonly Func<GridItem, string?> originalClass = new Original<GridItem, FormattableClass>() { Property = x => x.Class, Format = "X" }.Test();
private readonly Func<GridItem, string?> originalNonNullable = new Original<GridItem, FormattableStruct>() { Property = x => x.NonNullable, Format = "X" }.Test();
private readonly Func<GridItem, string?> originalNullable = new Original<GridItem, FormattableStruct?>() { Property = x => x.Nullable, Format = "X" }.Test();
private readonly Func<GridItem, string?> reflectionClass = new ReflectionTest<GridItem, FormattableClass>() { Property = x => x.Class, Format = "X" }.Test();
private readonly Func<GridItem, string?> reflectionNonNullable = new ReflectionTest<GridItem, FormattableStruct>() { Property = x => x.NonNullable, Format = "X" }.Test();
private readonly Func<GridItem, string?> reflectionNullable = new ReflectionTest<GridItem, FormattableStruct?>() { Property = x => x.Nullable, Format = "X" }.Test();
private readonly Func<GridItem, string?> formatterClass = new FormatterTest<GridItem, FormattableClass>() { Property = x => x.Class, Format = "X" }.Test();
private readonly Func<GridItem, string?> formatterNonNullable = new FormatterTest<GridItem, FormattableStruct>() { Property = x => x.NonNullable, Format = "X" }.Test();
private readonly Func<GridItem, string?> formatterNullable = new FormatterTest<GridItem, FormattableStruct?>() { Property = x => x.Nullable, Format = "X" }.Test();
private readonly Func<GridItem, string?> compiledExpressionClass = new CompiledExpressionTest<GridItem, FormattableClass>() { Property = x => x.Class, Format = "X" }.Test();
private readonly Func<GridItem, string?> compiledExpressionNonNullable = new CompiledExpressionTest<GridItem, FormattableStruct>() { Property = x => x.NonNullable, Format = "X" }.Test();
private readonly Func<GridItem, string?> compiledExpressionNullable = new CompiledExpressionTest<GridItem, FormattableStruct?>() { Property = x => x.Nullable, Format = "X" }.Test();
[Benchmark]
public void OriginalClass() => originalClass(gridItem);
[Benchmark]
public void OriginalNonNullable() => originalNonNullable(gridItem);
[Benchmark]
public void OriginalNullable() => originalNullable(gridItem);
[Benchmark]
public void ReflectionClass() => reflectionClass(gridItem);
[Benchmark]
public void ReflectionNonNullable() => reflectionNonNullable(gridItem);
[Benchmark]
public void ReflectionNullable() => reflectionNullable(gridItem);
[Benchmark]
public void FormatterClass() => formatterClass(gridItem);
[Benchmark]
public void FormatterNonNullable() => formatterNonNullable(gridItem);
[Benchmark]
public void FormatterNullable() => formatterNullable(gridItem);
[Benchmark]
public void CompiledExpressionClass() => compiledExpressionClass(gridItem);
[Benchmark]
public void CompiledExpressionNonNullable() => compiledExpressionNonNullable(gridItem);
[Benchmark]
public void CompiledExpressionNullable() => compiledExpressionNullable(gridItem);
}
class Original<TGridItem, TProp>
{
public Expression<Func<TGridItem, TProp>> Property { get; set; } = default!;
public string? Format { get; set; }
public Func<TGridItem, string?> Test()
{
Func<TGridItem, string?> cellTextFunc;
var compiledPropertyExpression = Property.Compile();
if (!string.IsNullOrEmpty(Format))
{
// TODO: Consider using reflection to avoid having to box every value just to call IFormattable.ToString
// For example, define a method "string Format<U>(Func<TGridItem, U> property) where U: IFormattable", and
// then construct the closed type here with U=TProp when we know TProp implements IFormattable
// If the type is nullable, we're interested in formatting the underlying type
var nullableUnderlyingTypeOrNull = Nullable.GetUnderlyingType(typeof(TProp));
if (!typeof(IFormattable).IsAssignableFrom(nullableUnderlyingTypeOrNull ?? typeof(TProp)))
{
throw new InvalidOperationException($"A '{nameof(Format)}' parameter was supplied, but the type '{typeof(TProp)}' does not implement '{typeof(IFormattable)}'.");
}
cellTextFunc = item => ((IFormattable?)compiledPropertyExpression!(item))?.ToString(Format, null);
}
else
{
cellTextFunc = item => compiledPropertyExpression!(item)?.ToString();
}
return cellTextFunc;
}
private string? FormatItem<U>(TGridItem item, Func<TGridItem, U> property) where U : IFormattable
{
return property(item)?.ToString(Format, null);
}
}
class ReflectionTest<TGridItem, TProp>
{
private static readonly MethodInfo formatItemMethodInfo = typeof(ReflectionTest<TGridItem, TProp>).GetMethod("FormatItem", BindingFlags.NonPublic | BindingFlags.Instance)!;
private static readonly MethodInfo formatItemNullableMethodInfo = typeof(ReflectionTest<TGridItem, TProp>).GetMethod("FormatItemNullable", BindingFlags.NonPublic | BindingFlags.Instance)!;
public Expression<Func<TGridItem, TProp>> Property { get; set; } = default!;
public string? Format { get; set; }
public Func<TGridItem, string?> Test()
{
Func<TGridItem, string?> cellTextFunc;
var compiledPropertyExpression = Property.Compile();
if (!string.IsNullOrEmpty(Format))
{
// If the type is nullable, we're interested in formatting the underlying type
var nullableUnderlyingTypeOrNull = Nullable.GetUnderlyingType(typeof(TProp));
if (!typeof(IFormattable).IsAssignableFrom(nullableUnderlyingTypeOrNull ?? typeof(TProp)))
{
throw new InvalidOperationException($"A '{nameof(Format)}' parameter was supplied, but the type '{typeof(TProp)}' does not implement '{typeof(IFormattable)}'.");
}
var formatter = (Func<TGridItem, Func<TGridItem, TProp>, string>)Delegate.CreateDelegate(
typeof(Func<TGridItem, Func<TGridItem, TProp>, string>),
this,
nullableUnderlyingTypeOrNull == null
? formatItemMethodInfo.MakeGenericMethod(typeof(TProp))
: formatItemNullableMethodInfo.MakeGenericMethod(nullableUnderlyingTypeOrNull));
cellTextFunc = item => formatter(item, compiledPropertyExpression);
}
else
{
cellTextFunc = item => compiledPropertyExpression!(item)?.ToString();
}
return cellTextFunc;
}
private string? FormatItem<U>(TGridItem item, Func<TGridItem, U> property) where U : IFormattable
{
return property(item)?.ToString(Format, null);
}
private string? FormatItemNullable<U>(TGridItem item, Func<TGridItem, U?> property) where U : struct, IFormattable
{
return property(item)?.ToString(Format, null);
}
}
public interface IFormatter<T>
{
string Format(T value, string? format, IFormatProvider? formatProvider = null);
}
public static class Formatter<T>
{
public static IFormatter<T>? Instance { get; }
static Formatter()
{
object? instance = null;
var underlying = Nullable.GetUnderlyingType(typeof(T));
if (typeof(IFormattable).IsAssignableFrom(underlying ?? typeof(T)))
{
if (typeof(T).IsValueType)
{
if (underlying is null)
{
instance = Activator.CreateInstance(typeof(SupportedValueTypeFormatter<>).MakeGenericType(typeof(T)));
}
else
{
instance = Activator.CreateInstance(typeof(SupportedNullableFormatter<>).MakeGenericType(underlying));
}
}
else
{
instance = Activator.CreateInstance(typeof(SupportedRefTypeFormatter<>).MakeGenericType(typeof(T)));
}
}
Instance = (IFormatter<T>?)instance;
}
}
internal sealed class SupportedNullableFormatter<T> : IFormatter<T?>
where T : struct, IFormattable
{
public string Format(T? value, string? format, IFormatProvider? formatProvider)
=> value is null ? "" : value.GetValueOrDefault().ToString(format, formatProvider);
}
internal sealed class SupportedValueTypeFormatter<T> : IFormatter<T>
where T : struct, IFormattable
{
public string Format(T value, string? format, IFormatProvider? formatProvider)
=> value.ToString(format, formatProvider);
}
internal sealed class SupportedRefTypeFormatter<T> : IFormatter<T>
where T : class, IFormattable
{
public string Format(T value, string? format, IFormatProvider? formatProvider)
=> value is null ? "" : value.ToString(format, formatProvider);
}
class FormatterTest<TGridItem, TProp>
{
public Expression<Func<TGridItem, TProp>> Property { get; set; } = default!;
public string? Format { get; set; }
public Func<TGridItem, string?> Test()
{
Func<TGridItem, string?> cellTextFunc;
var compiledPropertyExpression = Property.Compile();
if (!string.IsNullOrEmpty(Format))
{
// If the type is nullable, we're interested in formatting the underlying type
var nullableUnderlyingTypeOrNull = Nullable.GetUnderlyingType(typeof(TProp));
if (!typeof(IFormattable).IsAssignableFrom(nullableUnderlyingTypeOrNull ?? typeof(TProp)))
{
throw new InvalidOperationException($"A '{nameof(Format)}' parameter was supplied, but the type '{typeof(TProp)}' does not implement '{typeof(IFormattable)}'.");
}
cellTextFunc = item => Formatter<TProp>.Instance!.Format(compiledPropertyExpression!(item), Format, null);
}
else
{
cellTextFunc = item => compiledPropertyExpression!(item)?.ToString();
}
return cellTextFunc;
}
}
class CompiledExpressionTest<TGridItem, TProp>
{
private static readonly MethodInfo toStringMethod = typeof(IFormattable).GetMethod("ToString")!;
public Expression<Func<TGridItem, TProp>> Property { get; set; } = default!;
public string? Format { get; set; }
public Func<TGridItem, string?> Test()
{
Func<TGridItem, string?> cellTextFunc;
if (!string.IsNullOrEmpty(Format))
{
// If the type is nullable, we're interested in formatting the underlying type
var nullableUnderlyingTypeOrNull = Nullable.GetUnderlyingType(typeof(TProp));
if (!typeof(IFormattable).IsAssignableFrom(nullableUnderlyingTypeOrNull ?? typeof(TProp)))
{
throw new InvalidOperationException($"A '{nameof(Format)}' parameter was supplied, but the type '{typeof(TProp)}' does not implement '{typeof(IFormattable)}'.");
}
var propVar = Expression.Variable(typeof(TProp), "prop");
Expression toStringCallTarget = nullableUnderlyingTypeOrNull == null
? propVar
: Expression.Call(
propVar,
typeof(TProp).GetMethod("GetValueOrDefault", Type.EmptyTypes)!);
var toStringCall = Expression.Call(
toStringCallTarget,
toStringMethod,
Expression.Property(Expression.Constant(this), "Format"),
Expression.Constant(null, typeof(IFormatProvider)));
Expression conditionalCall = default(TProp) == null
? Expression.Condition(
Expression.Equal(propVar, Expression.Constant(null, typeof(TProp))),
Expression.Constant(null, typeof(string)),
toStringCall)
: toStringCall;
var block = Expression.Block(new[] { propVar },
Expression.Assign(propVar, Property.Body),
conditionalCall);
cellTextFunc = Expression.Lambda<Func<TGridItem, string?>>(block, Property.Parameters[0]).Compile();
}
else
{
cellTextFunc = item => Property.Compile()!(item)?.ToString();
}
return cellTextFunc;
}
}
public class GridItem
{
public FormattableClass Class { get; } = new FormattableClass();
public FormattableStruct NonNullable => new FormattableStruct();
public FormattableStruct? Nullable => new FormattableStruct();
}
public class FormattableClass : IFormattable
{
public string ToString(string? format, IFormatProvider? formatProvider) => "";
}
public struct FormattableStruct : IFormattable
{
public string ToString(string? format, IFormatProvider? formatProvider) => "";
}
public class Program
{
public static void Main(string[] args)
{
BenchmarkRunner.Run<Benchmark>();
}
}
}
With the results:
Method
Mean
Error
StdDev
Gen0
Allocated
OriginalClass
6.111 ns
0.1206 ns
0.1128 ns
-
-
OriginalNonNullable
7.568 ns
0.1793 ns
0.1677 ns
0.0038
24 B
OriginalNullable
54.260 ns
1.0880 ns
1.4893 ns
0.0038
24 B
ReflectionClass
6.750 ns
0.0630 ns
0.0590 ns
-
ReflectionNonNullable
4.710 ns
0.0514 ns
0.0456 ns
-
ReflectionNullable
7.374 ns
0.0819 ns
0.0726 ns
-
FormatterClass
14.054 ns
0.2079 ns
0.1843 ns
-
-
FormatterNonNullable
3.521 ns
0.0907 ns
0.0849 ns
-
-
FormatterNullable
7.156 ns
0.0889 ns
0.0832 ns
-
-
CompiledExpressionClass
2.919 ns
0.0864 ns
0.0888 ns
-
-
CompiledExpressionNonNullable
1.815 ns
0.0405 ns
0.0379 ns
-
-
CompiledExpressionNullable
1.799 ns
0.0577 ns
0.0686 ns
-
-
As you can see, the only solution which boxes is the original code. The reflection-based approach is faster than Marc's solution for classes, and about the same for structs. However, the expression-based approach is significantly faster than anything.
I've no idea where that extra cost in FormatterClass is coming from, but it's repeatable.
I'm deliberately ignoring the cost of constructing the Func<TGridItem, string> in this benchmark: the original code is obviously optimised for the case where Property rarely changes, and the cost of compiling Property is going to dominate anything else most likely.

I would go with a generic interface such as IFormatter<T> without a generic constraint, and a few private implementations with the necessary complaint, and use reflection internally to decide which private implementation to use - a simple version for everything except Nullable<T>, and a special-case for that; this is the same approach used by EqualityComparer<T>.Default.
Full example is below, noting that Formatter<T>.Instance will be null if the T doesn't support IFormattable (to allow testing, although this could also be handled separately).
The use of generics in the private implementation means we're using "constrained call", so: no boxing there. The special-casing of Nullable<T> means we can handle that too, with an additional indirection.
The important thing is that we only do the thinking once per type - testing whether the type is nullable, whether it implements IFormattable, etc - and this is then cached via a strategy instance. If we do the thinking every time: boxing would probably be cheaper!
Relevant usage for the sample provided (note you could probably simply with a non-generic static class with generic method so you can just do Formatter.Format(value, format, provider) and use generic inference from value for the rest):
// L50
if (Formatter<TProp>.Instance is null)
{
throw new InvalidOperationException($"A '{nameof(Format)}' parameter was supplied, but the type '{typeof(TProp)}' does not implement '{typeof(IFormattable)}'.");
}
_cellTextFunc = item => Formatter<TProp>.Instance.Format(compiledPropertyExpression!(item), Format, null);
Console.WriteLine(Formatter<int>.Instance!.Format(123, "G"));
Console.WriteLine(Formatter<int?>.Instance!.Format(123, "G"));
Console.WriteLine(Formatter<int?>.Instance!.Format(null, "G"));
Console.WriteLine(Formatter<NotFormattable?>.Instance is null);
struct NotFormattable { }
public interface IFormatter<T>
{
string Format(T value, string? format, IFormatProvider? formatProvider = null);
}
public static class Formatter<T>
{
public static IFormatter<T>? Instance { get; }
static Formatter()
{
object? instance = null;
var underlying = Nullable.GetUnderlyingType(typeof(T));
if (typeof(IFormattable).IsAssignableFrom(underlying ?? typeof(T)))
{
if (typeof(T).IsValueType)
{
if (underlying is null)
{
instance = Activator.CreateInstance(typeof(SupportedValueTypeFormatter<>).MakeGenericType(typeof(T)));
}
else
{
instance = Activator.CreateInstance(typeof(SupportedNullableFormatter<>).MakeGenericType(underlying));
}
}
else
{
instance = Activator.CreateInstance(typeof(SupportedRefTypeFormatter<>).MakeGenericType(typeof(T)));
}
}
Instance = (IFormatter<T>?)instance;
}
}
internal sealed class SupportedNullableFormatter<T> : IFormatter<T?>
where T : struct, IFormattable
{
public string Format(T? value, string? format, IFormatProvider? formatProvider)
=> value is null ? "" : value.GetValueOrDefault().ToString(format, formatProvider);
}
internal sealed class SupportedValueTypeFormatter<T> : IFormatter<T>
where T : struct, IFormattable
{
public string Format(T value, string? format, IFormatProvider? formatProvider)
=> value.ToString(format, formatProvider);
}
internal sealed class SupportedRefTypeFormatter<T> : IFormatter<T>
where T : class, IFormattable
{
public string Format(T value, string? format, IFormatProvider? formatProvider)
=> value is null ? "" : value.ToString(format, formatProvider);
}

Related

C# List that that accepts 2 or more enum types

I have these multiple enums used as Ids and want to use them in a single list.
public enum eUnit
{
Villager,
Warrior,
Wizard,
}
public enum eVehicle
{
Car,
Train,
Helicopter,
}
public enum eItem
{
Apple,
Steak,
Pizza,
}
Is something like the code below possible?
List<?enum?> myList = new List<?enum?>();
myList.Add(eUnit.Warrior);
myList.Add(eItem.Pizza);
if(myList[0].GetType() == typeof(eUnit))
DoStuff();
...
...
Other way is to use a discriminated union via OneOf library.
var myList = new List<OneOf<eUnit, eVehicle, eItem>>()
{
eUnit.Warrior, eVehicle.Car
};
myList[0].Switch(
unit => Console.WriteLine("Unit"),
vehicle => Console.WriteLine("Vehicle"),
item => Console.WriteLine("Item")
);
I made a sample
You could try using the type dynamic. Perhaps it would be overkill but it is up to you.
You can implement your own List<T>. Here is a simple example :
public class EnumList
{
private readonly List<Entry> _entriesList = new List<Entry>();
public object this[int index]
{
get
{
var item = _entriesList[index];
return Enum.Parse(item.Key, item.Value);
}
set
{
_entriesList[index] = new Entry(value.GetType(), value.ToString());
}
}
private class Entry
{
public Type Key { get; set; }
public string Value { get; set; }
public Entry(Type key, string value)
{
Key = key;
Value = value;
}
}
public void Add<TEnum>(TEnum item) where TEnum : struct, Enum
{
_entriesList.Add(new Entry(typeof(TEnum), item.ToString()));
}
public List<TEnum> Get<TEnum>() where TEnum : struct, Enum
{
return _entriesList.Where(x => x.Key == typeof(TEnum)).Select(x => Enum.TryParse(x.Value, out TEnum result) ? result : default).ToList();
}
public bool Exists<TEnum>(TEnum item) where TEnum : struct, Enum
{
return _entriesList.Any(x => x.Key == typeof(TEnum) && x.Value == item.ToString());
}
}
Usage :
var list = new EnumList();
list.Add(eUnit.Warrior);
list.Add(eItem.Pizza);
list.Add(eVehicle.Car);
list.Add(eVehicle.Helicopter);
list.Add(eUnit.Villager);
list.Add(eItem.Apple);
if((eUnit)list[0] == eUnit.Warrior || list.Exists(eUnit.Villager))
{
// do stuff
}
This is just a sample to help you implement your own, remember, you should always try to narrow the acceptable params by specifying the intended enums, you could go full generic though.
As mentioned by Andriy, using a discriminated union such as the OneOf, or Either can be useful in this case.
If you are just using the enums as an ID, you could also create various classes and utilize the type system for pattern matching. This is just a different pattern you could look into if you like pattern matching against specific items.
internal abstract class EnumType
{
protected EnumType(int value, string name)
{
if(value < 0) { throw new InvalidOperationException("Value cannot be less than 0."); }
this._value = value;
this._name = name ?? throw new ArgumentNullException(nameof(name));
}
public static implicit operator int(EnumType x)
=> x?._value ?? throw new ArgumentNullException(nameof(x));
public static implicit operator string(EnumType x)
=> x?._name ?? throw new ArgumentNullException(nameof(x));
private readonly int _value;
private readonly string _name;
}
internal sealed class eUnit : EnumType
{
private eUnit(int value, string name): base(value, name) { }
// operator overloads like |, &, ^, etc...
internal static readonly eUnit Villager = new eUnit(0, "Villager");
internal static readonly eUnit Warrior = new eUnit(1, "Warrior");
internal static readonly eUnit Wizard = new eUnit(2, "Wizard");
}
internal sealed class eItem : EnumType
{
private eItem(int value, string name): base(0, "Apple") { }
// operator overloads like |, &, ^, etc...
internal static readonly eItem Apple = new eItem(0, "Apple");
internal static readonly eItem Steak = new eItem(1, "Steak");
internal static readonly eItem Pizza = new eItem(2, "Pizza");
}
This would allow you to then write:
var myList = new List<EnumType>();
myList.Add(eUnit.Warrior);
myList.Add(eItem.Pizza);
if (myList[0] is eUnit eUnit)
{
DoStuff();
}
If you cared about further granularity, you could turn those static fields into classes as well, along the lines of:
internal abstract class eUnit : EnumType
{
private eUnit(int value, string name): base(value, name) { }
// operator overloads like |, &, ^, etc...
internal sealed class Villager : eUnit
{
private Villager(): base(0, "Villager") { }
internal static readonly Villager _ = new Villager();
}
internal sealed class Warrior : eUnit
{
private Warrior(): base(1, "Warrior") { }
internal static readonly Warrior _ = new Warrior();
}
internal sealed class Wizard : eUnit
{
private Wizard(): base(2, "Wizard") { }
internal static readonly Wizard _ = new Wizard();
}
}
internal abstract class eItem : EnumType
{
private eItem(int value, string name): base(0, "Apple") { }
// operator overloads like |, &, ^, etc...
//...
internal sealed class Pizza : eItem
{
private Pizza(): base(2, "Pizza") { }
internal static readonly Pizza _ = new Pizza();
}
}
Then your sample would be rewritten as:
var myList = new List<EnumType>();
myList.Add(eUnit.Warrior._);
myList.Add(eItem.Pizza._);
var result = myList[0] switch
{
eUnit eUnit => eUnit switch
{
eUnit.Villager villager => DoVillagerStuff(villager),
eUnit.Warrior warrior => DoWarriorStuff(warrior),
eUnit.Wizard wizard => DoWizardStuff(wizard),
_ => throw new InvalidOperationException("Unknonwn eItem");
},
eItem eItem = eItem switch
{
eItem.Pizza pizza => DoPizzaStuff(pizza),
_ => throw new InvalidOperationException("Unsupported eItem")
}
};
You could have a look at the ArrayList class:
public enum eUnit
{
Villager,
Warrior,
Wizard,
}
public enum eVehicle
{
Car,
Train,
Helicopter,
}
public enum eItem
{
Apple,
Steak,
Pizza,
}
void Main()
{
var myList = new ArrayList();
myList.Add(eUnit.Warrior);
myList.Add(eItem.Pizza);
for (int i = 0; i < myList.Count; i++)
{
var element = myList[i];
if (element is eUnit unit)
Console.WriteLine($"Element at index {i} is eUnit {unit}");
else if (element is eVehicle vehicle)
Console.WriteLine($"Element at index {i} is eVehicle {vehicle}");
else if (element is eItem item)
Console.WriteLine($"Element at index {i} is eItem {item}");
else
Console.WriteLine($"Element at index {i} is another type");
}
}

Polymorphic object creation without IF condition

I have an abstract class like this:
public abstract class Records
{
public string Type;
public string Source;
public int Value;
protected Records(string type, string source, int value)
{
Type = type;
Source = source;
Value = value;
}
}
I would like to create many classes inheriting this class, and filling their Type field with a value coming from a static class like this:
public static class ContentTypesString
{
public static string DocumentNew { get { return "Document - New this Month"; }}
public static string HeadlinesNew { get { return "Headlines - New this Month"; }}
etc...
}
I would like to be able to create those child classes without having a test "if foo == "document" then type = ContentTypesString.DocumentNew" or an equivalent switch case (I really have a lot of cases)
Is there a design pattern that suits my needs?
EDIT : As several people pointed out, i should show how i create my instances.
private delegate SPListItemCollection Query(SPWeb web, DateTime startDate, DateTime endDate);
private readonly Query _queries;
#region Constructors
public QueryHandler(SPWeb web, DateTime startTimeSelectedDate, DateTime endTimeSelectedDate)
{
if (web == null) throw new ArgumentNullException("web");
_web = web;
_startTimeSelectedDate = startTimeSelectedDate;
_endTimeSelectedDate = endTimeSelectedDate;
RecordsList = new List<Records>();
// Query Invocation List
_queries = NumberPagePerMonthQuery.PreparedQuery;
_queries += NumberDocumentsPerMonthQuery.PreparedQuery;
_queries += NumberHeadlinesPerMonthQuery.PreparedQuery;
_queries += NumberLeaderboxPerMonthQuery.PreparedQuery;
_queries += NumberNewsPerMonthQuery.PreparedQuery;
_queries += NumberPagesModifiedPerMonthQuery.PreparedQuery;
_queries += NumberPicturesPerMonthQuery.PreparedQuery;
_queries += NumberTeasingPerMonthQuery.PreparedQuery;
}
#endregion Constructors
#region Public Methods
// what about NullReferenceException ? C#6 : item?.Foreach(item => {}); ?
/*** NO C#6 compiler in VS2012... ***/
public void Queries()
{
foreach (var del in _queries.GetInvocationList())
{
var queryresult =
(SPListItemCollection) del.DynamicInvoke(_web, _startTimeSelectedDate, _endTimeSelectedDate);
RecordsList.Add(new Records(del.Method.Name, _web.Title, queryresult.Count));
}
}
EDIT² :
The solution i chose
public List<IQuery> QueryList { get; } // no delegate anymore, and static classes became implementations of IQuery interface.
#region Constructors
public QueryHandler(SPWeb web, DateTime startTimeSelectedDate, DateTime endTimeSelectedDate)
{
if (web == null) throw new ArgumentNullException("web");
_web = web;
_startTimeSelectedDate = startTimeSelectedDate;
_endTimeSelectedDate = endTimeSelectedDate;
RecordsList = new List<Records>();
QueryList = new List<IQuery>
{
new NumberDocumentsPerMonthQuery(),
new NumberHeadlinesPerMonthQuery(),
new NumberLeaderboxPerMonthQuery(),
new NumberNewsPerMonthQuery(),
new NumberPagePerMonthQuery(),
new NumberPagesModifiedPerMonthQuery(),
new NumberPicturesPerMonthQuery(),
new NumberTeasingPerMonthQuery()
};
}
#endregion Constructors
#region Public Methods
// what about NullReferenceException ? C#6 : item?.Foreach(item => {}); ?
/*** NO C#6 compiler in VS2012... ***/
public void Queries()
{
foreach (var query in QueryList)
{
var queryresult = query.PreparedQuery(_web, _startTimeSelectedDate, _endTimeSelectedDate);
RecordsList.Add(query.CreateRecord(_web.Title, queryresult.Count));
}
}
Record class follow the implementation suggested by #dbraillon
Implementation of IQuery interface were added the method :
public Records CreateRecord(string source, int value)
{
return new ModifiedPagesPerMonthRecord(source, value); //or another child of Record class.
}
And voilĂ . Thank you all for the help.
You want to make collection of records, by string code of object type, and parameters.
One of many way to do it - use builder.
Firstly we need to configurate builder:
var builder = new RecordBuilder()
.RegisterBuilder("document", (source, value) => new Document(source, value))
.RegisterBuilder("headlines", (source, value) => new Headlines(source, value));
here we specify how to build record with code "document" and "headlines".
To build a record call:
builder.Build("document", "source", 1);
Builder code can by something like this
(here we look if we know how to build record of the passed type and make it):
public class RecordBuilder
{
public Records Build(string code, string source, int value)
{
Func<string, int, Records> buildAction;
if (recordBuilders.TryGetValue(code, out buildAction))
{
return buildAction(source, value);
}
return null;
}
public RecordBuilder RegisterBuilder(string code, Func<string, int, Records> buildAction)
{
recordBuilders.Add(code, buildAction);
return this;
}
private Dictionary<string, Func<string, int, Records>> recordBuilders = new Dictionary<string, Func<string, int, Records>> ();
}
public class Document : Records
{
public Document(string source, int value) : base(ContentTypesString.DocumentNew, source, value)
{
}
}
public class Headlines : Records
{
public Headlines(string source, int value) : base(ContentTypesString.HeadlinesNew, source, value)
{
}
}
Is that what you need ?
public abstract class Records
{
public string Type;
public string Source;
public int Value;
protected Records(string type, string source, int value)
{
Type = type;
Source = source;
Value = value;
}
}
public class DocumentRecords : Records
{
public DocumentRecords(string source, int value)
: base(ContentTypesString.DocumentNew, source, value) // use here
{
}
}
public class HeadlinesRecords : Records
{
public HeadlinesRecords(string source, int value)
: base(ContentTypesString.HeadlinesNew, source, value) // use here
{
}
}
public static class ContentTypesString
{
public static string DocumentNew { get { return "Document - New this Month"; } }
public static string HeadlinesNew { get { return "Headlines - New this Month"; } }
}

Proper run-time construction pattern for large number of objects

What is the best way to create derived objects at run-time while adhering to LSP and always keeping the objects in a valid state.
I'm fairly new to construction patterns such as Factory and Builder and most of the examples I find are very simplistic. Here is my scenario:
I have one base class (some things left out for brevity):
public abstract BaseClass
{
public string Property1 { get; set ... null guard; }
public string Property2 { get; set ... conditional null guard; }
public virtual bool RequiresProperty2 => false;
protected BaseClass(string property1)
{
null guard
Property1 = property1;
}
}
I have 50+ derived classes. Some of which require prop2 some of which don't. The ones that require prop2 have a constructor that forces prop2 to be passed in, enforcing that all BaseClass derived objects are in a valid state upon construction. I'm also trying to adhere to LSP and I'm using Castle Windsor for dependency injection.
The solution I've come up with is to create a factory that returns a builder:
public interface IFactory
{
IBuilder Create(string type);
}
public interface IBuilder
{
IBuilder SetProperty1(string property);
IBuilder SetProperty2(string property);
BaseClass Build();
}
The concrete implementation of the factory loads all the types that inherit from BaseClass through reflection. When you call Factory.Create(...) you pass it a string which is the string name of the type you want to create. Then the factory creates a builder passing the appropriate Type to the builder's constructor. The builder looks like so:
public sealed class ConcreteBuilder : IBuilder
{
private static Type ClassType = typeof(BaseClass);
private static readonly ConcurrentDictionary<Type, Delegate>
ClassConstructors = new ConcurrentDictionary<Type, Delegate>();
private readonly Type type;
private string property1;
private string property2;
public ConcreteBuilder(Type type)
{
if (type == null) throw new ArgumentNullException(nameof(type));
if (!type.IsSubclassOf(ClassType))
{
throw new ArgumentException("Must derive from BaseClass.");
}
this.type = type;
}
public IBuilder SetProperty1(string property)
{
this.property1 = property;
return this;
}
public IBuilder SetProperty2(string property)
{
this.property2 = property;
return this;
}
public BaseClass Build()
{
var arguments = BuildArguments();
Delegate ctor;
if (ClassConstructors.TryGetValue(this.type, out ctor))
{
return (BaseClass)ctor.DynamicInvoke(arguments);
}
return (BaseClass)GetConstructor(arguments).DynamicInvoke(arguments);
}
private object[] BuildArguments()
{
var args = new List<object>();
if (!string.IsNullOrEmpty(this.property1))
{
args.Add(this.property1);
}
if (!string.IsNullOrEmpty(this.property2))
{
args.Add(this.property2);
}
return args.ToArray();
}
private Delegate GetConstructor(object[] arguments)
{
var constructors = this.type.GetConstructors();
foreach (var constructor in constructors)
{
var parameters = constructor.GetParameters();
var parameterTypes = parameters.Select(p => p.ParameterType).ToArray();
if (parameterTypes.Length != arguments.Length + 1) continue;
if (!parameterTypes.Zip(arguments, TestArgumentForParameter).All(x => x))
{
continue;
}
var parameterExpressions = parameters.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
var callConstructor = Expression.New(constructor, parameterExpressions);
var ctor = Expression.Lambda(callConstructor, parameterExpressions).Compile();
ClassConstructors.TryAdd(this.type, ctor);
return ctor;
}
throw new MissingMethodException("No constructor found");
}
private static bool TestArgumentForParameter(Type parameterType, object argument)
{
return (argument == null && !parameterType.IsValueType) || (parameterType.IsInstanceOfType(argument));
}
}
Is there a better way to do this? Am I going about this the right way? I know DynamicInvoke is slow. Should I be going about this differently?

type casting classes mimiking enums

I use a 3rd party application for output. There are several int properties and I would like to handle the different properties' int values via enum.
Property1 could be 0,1,2
Property2 could be 0,1
Property3 could be 1,2
I think I should have enum inheritance which is not option in c#.
So I solved it by using classes (I'm using Tono Nam's answer from another topic to this end: https://stackoverflow.com/a/23430174/4273304).
public class MyEnum : IEquatable<MyEnum>
{
public static readonly MyEnum Undefined = new MyEnum(-1, "Undefined");
public int Value { get; private set; }
public string Name { get; private set; }
protected MyEnum(int value, string name)
{
this.Value = value;
this.Name = name;
}
public bool Equals(MyEnum b)
{
return this.Name == b.Name && this.Value == b.Value;
}
public override string ToString()
{
return this.Name;
}
public static T Parse<T>(int value)
{
object obj;
Type t_type = typeof(T);
var fiList = t_type.GetFields(BindingFlags.Public | BindingFlags.Static).Where(f => f.FieldType == typeof(T)).ToArray();
foreach(FieldInfo en in fiList)
{
object tmp = en.GetValue(null);
if (((MyEnum)tmp).Value == value)
return (T)tmp;
}
obj = MyEnum.Undefined;
return (T)obj;
}
}
public class MyEnumChild1 : MyEnum
{
public static readonly MyEnumChild1 A = new MyEnumChild1(0, "A");
public static readonly MyEnumChild1 B = new MyEnumChild1(1, "B");
private MyEnumChild1(int value, string name)
: base(value, name)
{
}
}
public class MyEnumChild2 : MyEnum
{
public static readonly MyEnumChild2 A = new MyEnumChild2(0, "A");
public static readonly MyEnumChild2 C = new MyEnumChild2(1, "C");
private MyEnumChild2(int value, string name)
: base(value, name)
{
}
}
public class MyEnumChild3 : MyEnum
{
public static readonly MyEnumChild3 D = new MyEnumChild3(0, "D");
public static readonly MyEnumChild3 E = new MyEnumChild3(1, "E");
private MyEnumChild3(int value, string name)
: base(value, name)
{
}
}
This solution serves my purposes, but I dont know how to cast an int to MyEnumChild1.
I created a parser method:
MyEnumChild1 MEC1 = MyEnum.Parse <MyEnumChild1>(1);
It seems to work fine, MEC1 is MyEnumChild1.B now, but I'm not sure of it.
How safe do you think my parser method is? Are there any mistakes in this code or can I use it safely?
Do you know any better, elegant or simpler solution for the cast?
First, your Parse method should put a constraint on T:
public static T Parse<T>(int value) where T : MyEnum
Second, you can make it protected instead and implement a casting operator in each of the derived enums this way:
public static explicit operator MyEnumChild1(int value)
{
return Parse<MyEnumChild1>(value);
}
And use it in a more classic way:
MyEnumChild1 mec1 = (MyEnumChild1)1

How can I generate this property implementation using Expressions instead of emitting IL?

I'm trying to generate classes at runtime that implement property getters with a body that calls a method on the generated class's base class. Here's an example of a simple interface, along with a hand-written implementation that I'm trying to duplicate and the base class.
public interface IGenerated : IBase { decimal Property1 { get; } }
public class GeneratedByHand : ImplBase<IGenerated> {
public decimal Property1 { get { return Get(s => s.Property1); } }
}
public interface IBase { string _KeyPrefix { get; set; } }
public abstract class ImplBase<T> : IBase
where T : IBase
{
public virtual string _KeyPrefix { get; set; }
protected virtual TResult Get<TResult>(Expression<Func<T, TResult>> property) {
return GetValue<TResult>(GetPropertyName(property));
}
private string GetPropertyName<TResult>(Expression<Func<T, TResult>> property) {
return ""; // reflection stuff to get name from property expression goes here
}
private TResult GetValue<TResult>(string keyPart) {
return default(TResult); // does something like: return ReallyGetValue<TResult>(_KeyPrefix + keyPart);
}
}
I have a working implementation of the generator that emits IL to build the method, but if I can do it with Expressions I think that will be easier to expand and maintain. I will need to look for custom attributes on the property definitions and use that to call different method overloads on the base class in the property implementations.
Here's where I've gotten building an expression for the property get implementation. What I don't really understand is building the Call expression, if I'm setting it up correctly to do the equivalent of this.Get() or base.Get(). Right now it throws a System.ArgumentException : Invalid argument value Parameter name: method at CompileToMethod
public void CreateExpressionForGetMethod(MethodBuilder getBuilder, Type interfaceType, Type baseType, PropertyInfo property, MethodInfo getMethod)
{
var settingsParam = Expression.Parameter(interfaceType, "s");
var propGetterExpr = Expression.Property(settingsParam, property);
var propGetterExprFuncType = typeof(Func<,>).MakeGenericType(interfaceType, property.PropertyType);
var propGetterLambda = Expression.Lambda(propGetterExprFuncType, propGetterExpr, settingsParam);
var baseGetMethodInfo =
baseType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(m => {
var parameters = m.GetParameters();
return m.Name == "Get" &&
parameters != null && parameters.Count() == 1 && parameters[0].ParameterType != typeof(string);
})
.First().MakeGenericMethod(property.PropertyType);
var getExprType = typeof(Expression<>).MakeGenericType(propGetterExprFuncType);
var getExprParam = Expression.Parameter(getExprType, "expression");
var getCallExpr = Expression.Call(Expression.Parameter(baseType, "inst"), baseGetMethodInfo, propGetterLambda);
var getFuncType = typeof(Func<,>).MakeGenericType(getExprType, property.PropertyType);
var propLambda = Expression.Lambda(getFuncType, getCallExpr, getExprParam);
propLambda.CompileToMethod(getBuilder);
}
I'm not really sure where to go from here. I've tried a few other variations of arguments to Expression.Call, but everything else had Call throwing exceptions for the parameters being the wrong types.
Here's a buildable version of all the sample code I'm working with, including the working IL emitter:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using NUnit.Framework;
namespace ExpressionGenerationTest
{
[TestFixture]
public class UnitTests
{
[Test]
public void CreateAndSaveAssembly()
{
var implGenerator = new ImplBuilder();
var generatedType = implGenerator.CreateImplementation(typeof(IGenerated));
implGenerator.SaveAssembly();
}
}
public interface IBase { string _KeyPrefix { get; set; } }
public abstract class ImplBase<T> : IBase
where T : IBase
{
public virtual string _KeyPrefix { get; set; }
protected virtual TResult Get<TResult>(Expression<Func<T, TResult>> property) { return GetValue<TResult>(GetPropertyName(property)); }
private string GetPropertyName<TResult>(Expression<Func<T, TResult>> property) { return ""; } // reflection stuff to get name from property expression goes here
private TResult GetValue<TResult>(string keyPart) { return default(TResult); } // does something like: return ReallyGetValue(_KeyPrefix + keyPart);
}
public interface IGenerated : IBase { decimal Property1 { get; } }
public class GeneratedByHand : ImplBase<IGenerated> { public decimal Property1 { get { return Get(s => s.Property1); } } }
public class ImplBuilder
{
private const string _assemblyNameBase = "ExpressionGenerationTest.Impl";
public static ImplBuilder Default { get { return _default.Value; } }
private static readonly Lazy<ImplBuilder> _default = new Lazy<ImplBuilder>(() => new ImplBuilder());
private ConcurrentDictionary<Type, Type> _types = new ConcurrentDictionary<Type, Type>();
private AssemblyBuilder _assemblyBuilder = null;
private volatile ModuleBuilder _moduleBuilder = null;
private object _lock = new object();
private void EnsureInitialized()
{
if (_moduleBuilder == null) {
lock (_lock) {
if (_moduleBuilder == null) {
_assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(_assemblyNameBase), AssemblyBuilderAccess.RunAndSave);
_moduleBuilder = _assemblyBuilder.DefineDynamicModule(_assemblyBuilder.GetName().Name, _assemblyNameBase + ".dll");
}
}
}
}
public void SaveAssembly() { _assemblyBuilder.Save(_assemblyNameBase + ".dll"); }
public TSettings CreateInstance<TSettings>() { return (TSettings)Activator.CreateInstance(_types.GetOrAdd(typeof(TSettings), CreateImplementation)); }
public void CreateImplementations(IEnumerable<Type> types) { foreach (var t in types) _types.GetOrAdd(t, InternalCreateImplementation); }
public Type CreateImplementation(Type interfaceType) { return _types.GetOrAdd(interfaceType, InternalCreateImplementation); }
private Type InternalCreateImplementation(Type interfaceType)
{
EnsureInitialized();
var baseType = typeof (ImplBase<>).MakeGenericType(interfaceType);
var typeBuilder = _moduleBuilder.DefineType(
(interfaceType.IsInterface && interfaceType.Name.StartsWith("I")
? interfaceType.Name.Substring(1)
: interfaceType.Name) + "Impl",
TypeAttributes.Public | TypeAttributes.Class |
TypeAttributes.AutoClass | TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
baseType,
new [] {interfaceType});
foreach (var p in GetPublicProperties(interfaceType).Where(pi => pi.DeclaringType != typeof(IBase))) {
var iGet = p.GetGetMethod();
if (iGet != null) {
var getBuilder =
typeBuilder.DefineMethod(iGet.Name,
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
p.PropertyType, Type.EmptyTypes);
//EmitILForGetMethod(getBuilder, interfaceType, baseType, p, iGet);
CreateExpressionForGetMethod(getBuilder, interfaceType, baseType, p, iGet);
typeBuilder.DefineMethodOverride(getBuilder, iGet);
}
}
var implementationType = typeBuilder.CreateType();
return implementationType;
}
public void CreateExpressionForGetMethod(MethodBuilder getBuilder, Type interfaceType, Type baseType, PropertyInfo property, MethodInfo getMethod)
{
var settingsParam = Expression.Parameter(interfaceType, "s");
var propGetterExpr = Expression.Property(settingsParam, property);
var propGetterExprFuncType = typeof(Func<,>).MakeGenericType(interfaceType, property.PropertyType);
var propGetterLambda = Expression.Lambda(propGetterExprFuncType, propGetterExpr, settingsParam);
var baseGetMethodInfo =
baseType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(m => {
var parameters = m.GetParameters();
return m.Name == "Get" &&
parameters != null && parameters.Count() == 1 && parameters[0].ParameterType != typeof(string);
})
.First().MakeGenericMethod(property.PropertyType);
var getExprType = typeof(Expression<>).MakeGenericType(propGetterExprFuncType);
var getExprParam = Expression.Parameter(getExprType, "expression");
var getCallExpr = Expression.Call(Expression.Parameter(baseType, "inst"), baseGetMethodInfo, propGetterLambda);
var getFuncType = typeof(Func<,>).MakeGenericType(getExprType, property.PropertyType);
var propLambda = Expression.Lambda(getFuncType, getCallExpr, getExprParam);
propLambda.CompileToMethod(getBuilder);
}
public void EmitILForGetMethod(MethodBuilder getBuilder, Type interfaceType, Type baseType, PropertyInfo property, MethodInfo getMethod)
{
var getGen = getBuilder.GetILGenerator();
var retVal = getGen.DeclareLocal(property.PropertyType);
var expParam = getGen.DeclareLocal(typeof(ParameterExpression));
var expParams = getGen.DeclareLocal(typeof(ParameterExpression[]));
getGen.Emit(OpCodes.Ldarg_0);
getGen.Emit(OpCodes.Ldtoken, interfaceType);
getGen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
getGen.Emit(OpCodes.Ldstr, "s");
getGen.Emit(OpCodes.Call, typeof(Expression).GetMethod("Parameter", new [] {typeof(Type), typeof(string)}));
getGen.Emit(OpCodes.Stloc, expParam);
getGen.Emit(OpCodes.Ldloc, expParam);
getGen.Emit(OpCodes.Ldtoken, getMethod);
getGen.Emit(OpCodes.Call, typeof(MethodBase).GetMethod("GetMethodFromHandle", new [] {typeof(RuntimeMethodHandle)}, null));
getGen.Emit(OpCodes.Castclass, typeof(MethodInfo));
getGen.Emit(OpCodes.Call, typeof(Expression).GetMethod("Property", new[] {typeof(Expression), typeof(MethodInfo)}));
getGen.Emit(OpCodes.Ldc_I4_1);
getGen.Emit(OpCodes.Newarr, typeof(ParameterExpression));
getGen.Emit(OpCodes.Stloc, expParams);
getGen.Emit(OpCodes.Ldloc, expParams);
getGen.Emit(OpCodes.Ldc_I4_0);
getGen.Emit(OpCodes.Ldloc, expParam);
getGen.Emit(OpCodes.Stelem_Ref);
getGen.Emit(OpCodes.Ldloc, expParams);
var lambdaMethodInfo =
typeof(Expression).GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(x => {
var parameters = x.GetParameters();
return x.Name == "Lambda" &&
x.IsGenericMethodDefinition &&
parameters.Count() == 2 &&
parameters[0].ParameterType == typeof(Expression) &&
parameters[1].ParameterType == typeof(ParameterExpression[]);
}).FirstOrDefault();
var lambdaFuncType = typeof(Func<,>);
lambdaFuncType = lambdaFuncType.MakeGenericType(interfaceType, property.PropertyType);
lambdaMethodInfo = lambdaMethodInfo.MakeGenericMethod(lambdaFuncType);
getGen.Emit(OpCodes.Call, lambdaMethodInfo);
var baseGetMethodInfo =
baseType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(m => {
var parameters = m.GetParameters();
return m.Name == "Get" &&
parameters != null && parameters.Count() == 1 && parameters[0].ParameterType != typeof(string);
}).FirstOrDefault();
baseGetMethodInfo = baseGetMethodInfo.MakeGenericMethod(property.PropertyType);
getGen.Emit(OpCodes.Callvirt, baseGetMethodInfo);
getGen.Emit(OpCodes.Stloc_0);
var endOfMethod = getGen.DefineLabel();
getGen.Emit(OpCodes.Br_S, endOfMethod);
getGen.MarkLabel(endOfMethod);
getGen.Emit(OpCodes.Ldloc_0);
getGen.Emit(OpCodes.Ret);
}
// from http://stackoverflow.com/a/2444090/224087
public static PropertyInfo[] GetPublicProperties(Type type)
{
if (!type.IsInterface)
return type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
var propertyInfos = new List<PropertyInfo>();
var considered = new List<Type>();
var queue = new Queue<Type>();
considered.Add(type);
queue.Enqueue(type);
while (queue.Count > 0) {
var subType = queue.Dequeue();
foreach (var subInterface in subType.GetInterfaces()) {
if (considered.Contains(subInterface))
continue;
considered.Add(subInterface);
queue.Enqueue(subInterface);
}
var typeProperties = subType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
var newPropertyInfos = typeProperties.Where(x => !propertyInfos.Contains(x));
propertyInfos.InsertRange(0, newPropertyInfos);
}
return propertyInfos.ToArray();
}
}
}
What I don't really understand is building the Call expression, if I'm setting it up correctly to do the equivalent of this.Get() or base.Get().
If you are calling a virtual method, then this.Get(), which accesses the most-derived override (which could even be defined in a descendant of the current class), uses the callvirt instruction. And it doesn't matter what type you reflect against to get the MethodInfo, because they all share the same virtual table slot.
To emit base.Get(), you must
use the call instruction
reflect against the base class type
Because callvirt does some extra things besides v-table lookup, including a null pointer check, the C# compiler uses it for all virtual and non-virtual calls, except those involving the base keyword.
In particular, anonymous delegates and lambdas can't make use of the base keyword, since only descendant types can make non-virtual calls to virtual methods (at least in verifiable code), and the lambda is actually hosted by a closure type.
So unfortunately for your use case, there's no way to express a base call using lambda notation or expression trees. Expression.CompileToMethod only generates callvirt. Well, that isn't exactly correct. It generates call for calls to static methods and instance methods of value types. But instance methods of reference types use only callvirt. You can see this in System.Linq.Expressions.Compiler.LambdaCompiler.UseVirtual
Thanks #hvd for confirming this based on comments found in the Microsoft Reference Source for UseVirtual

Categories

Resources