How to dynamic new Anonymous Class? - c#

In C# 3.0 you can create anonymous class with the following syntax
var o1 = new { Id = 1, Name = "Foo" };
Is there a way to dynamic create these anonymous class to a variable?
Example:
var o1 = new { Id = 1, Name = "Foo" };
var o2 = new { SQ = 2, Birth = DateTime.Now };
Dynamic create Example:
var o1 = DynamicNewAnonymous(new NameValuePair("Id", 1), new NameValuePair("Name", "Foo"));
var o2 = DynamicNewAnonymous(new NameValuePair("SQ", 2), new NameValuePair("Birth",
DateTime.Now));
Beacuse I need to do:
dynamic o1 = new ExpandObject();
o1."ID" = 1; <--"ID" is dynamic name
o1."Name" = "Foo"; <--"Name" is dynamic name
And Scene1:
void ShowPropertiesValue(object o)
{
Type oType = o.GetType();
foreach(var pi in oType.GetProperties())
{
Console.WriteLine("{0}={1}", pi.Name, pi.GetValue(o, null));
}
}
if I call:
dynamic o1 = new ExpandObject();
o1.Name = "123";
ShowPropertiesValue(o1);
It can't show the result:
Name = 123
And also I how to Convert the ExpandoObject to AnonymouseType ?
Type type = o1.GetType();
type.GetProperties(); <--I hope it can get all property of o1
Last, I modify ShowPropertiesValue() method
void ShowPropertiesValue(object o)
{
if( o is static object ) <--How to check it is dynamic or static object?
{
Type oType = o.GetType();
foreach(var pi in oType.GetProperties())
{
Console.WriteLine("{0}={1}", pi.Name, pi.GetValue(o, null));
}
}
else if( o is dynamic object ) <--How to check it is dynamic or static object?
{
foreach(var pi in ??? ) <--How to get common dynamic object's properties info ?
{
Console.WriteLine("{0}={1}", pi.Name, pi.GetValue(o, null));
}
}
}
How to implement DynamicNewAnonymous method or how to modify the ShowPropertiesValue()?
My motivations is:
dynamic o1 = new MyDynamic();
o1.Name = "abc";
Type o1Type = o1.GetType();
var props = o1Type.GetProperties(); <--I hope can get the Name Property
If i can hook dynamicObject's GetType Method, and Compel convert to strongly-typed Type.
The above Seamless code can work fine.

Anonymous types are just regular types that are implicitly declared. They have little to do with dynamic.
Now, if you were to use an ExpandoObject and reference it through a dynamic variable, you could add or remove fields on the fly.
edit
Sure you can: just cast it to IDictionary<string, object>. Then you can use the indexer.
You use the same casting technique to iterate over the fields:
dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
foreach (var property in (IDictionary<string, object>)employee)
{
Console.WriteLine(property.Key + ": " + property.Value);
}
// This code example produces the following output:
// Name: John Smith
// Age: 33
The above code and more can be found by clicking on that link.

You can create an ExpandoObject like this:
IDictionary<string,object> expando = new ExpandoObject();
expando["Name"] = value;
And after casting it to dynamic, those values will look like properties:
dynamic d = expando;
Console.WriteLine(d.Name);
However, they are not actual properties and cannot be accessed using Reflection. So the following statement will return a null:
d.GetType().GetProperty("Name")

Of cause it's possible to create dynamic classes using very cool ExpandoObject class.
But recently I worked on project and faced that Expando Object is serealized in not the same format on xml as an simple Anonymous class, it was pity =( , that is why I decided to create my own class and share it with you. It's using reflection and dynamic directive , builds Assembly, Class and Instance truly dynamicly. You can add, remove and change properties that is included in your class on fly
Here it is :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using static YourNamespace.DynamicTypeBuilderTest;
namespace YourNamespace
{
/// This class builds Dynamic Anonymous Classes
public class DynamicTypeBuilderTest
{
///
/// Create instance based on any Source class as example based on PersonalData
///
public static object CreateAnonymousDynamicInstance(PersonalData personalData, Type dynamicType, List<ClassDescriptorKeyValue> classDescriptionList)
{
var obj = Activator.CreateInstance(dynamicType);
var propInfos = dynamicType.GetProperties();
classDescriptionList.ForEach(x => SetValueToProperty(obj, propInfos, personalData, x));
return obj;
}
private static void SetValueToProperty(object obj, PropertyInfo[] propInfos, PersonalData aisMessage, ClassDescriptorKeyValue description)
{
propInfos.SingleOrDefault(x => x.Name == description.Name)?.SetValue(obj, description.ValueGetter(aisMessage), null);
}
public static dynamic CreateAnonymousDynamicType(string entityName, List<ClassDescriptorKeyValue> classDescriptionList)
{
AssemblyName asmName = new AssemblyName();
asmName.Name = $"{entityName}Assembly";
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule($"{asmName.Name}Module");
TypeBuilder typeBuilder = moduleBuilder.DefineType($"{entityName}Dynamic", TypeAttributes.Public);
classDescriptionList.ForEach(x => CreateDynamicProperty(typeBuilder, x));
return typeBuilder.CreateTypeInfo().AsType();
}
private static void CreateDynamicProperty(TypeBuilder typeBuilder, ClassDescriptorKeyValue description)
{
CreateDynamicProperty(typeBuilder, description.Name, description.Type);
}
///
///Creation Dynamic property (from MSDN) with some Magic
///
public static void CreateDynamicProperty(TypeBuilder typeBuilder, string name, Type propType)
{
FieldBuilder fieldBuider = typeBuilder.DefineField($"{name.ToLower()}Field",
propType,
FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(name,
PropertyAttributes.HasDefault,
propType,
null);
MethodAttributes getSetAttr =
MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.HideBySig;
MethodBuilder methodGetBuilder =
typeBuilder.DefineMethod($"get_{name}",
getSetAttr,
propType,
Type.EmptyTypes);
ILGenerator methodGetIL = methodGetBuilder.GetILGenerator();
methodGetIL.Emit(OpCodes.Ldarg_0);
methodGetIL.Emit(OpCodes.Ldfld, fieldBuider);
methodGetIL.Emit(OpCodes.Ret);
MethodBuilder methodSetBuilder =
typeBuilder.DefineMethod($"set_{name}",
getSetAttr,
null,
new Type[] { propType });
ILGenerator methodSetIL = methodSetBuilder.GetILGenerator();
methodSetIL.Emit(OpCodes.Ldarg_0);
methodSetIL.Emit(OpCodes.Ldarg_1);
methodSetIL.Emit(OpCodes.Stfld, fieldBuider);
methodSetIL.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(methodGetBuilder);
propertyBuilder.SetSetMethod(methodSetBuilder);
}
public class ClassDescriptorKeyValue
{
public ClassDescriptorKeyValue(string name, Type type, Func<PersonalData, object> valueGetter)
{
Name = name;
ValueGetter = valueGetter;
Type = type;
}
public string Name;
public Type Type;
public Func<PersonalData, object> ValueGetter;
}
///
///Your Custom class description based on any source class for example
/// PersonalData
public static IEnumerable<ClassDescriptorKeyValue> GetAnonymousClassDescription(bool includeAddress, bool includeFacebook)
{
yield return new ClassDescriptorKeyValue("Id", typeof(string), x => x.Id);
yield return new ClassDescriptorKeyValue("Name", typeof(string), x => x.FirstName);
yield return new ClassDescriptorKeyValue("Surname", typeof(string), x => x.LastName);
yield return new ClassDescriptorKeyValue("Country", typeof(string), x => x.Country);
yield return new ClassDescriptorKeyValue("Age", typeof(int?), x => x.Age);
yield return new ClassDescriptorKeyValue("IsChild", typeof(bool), x => x.Age < 21);
if (includeAddress)
yield return new ClassDescriptorKeyValue("Address", typeof(string), x => x?.Contacts["Address"]);
if (includeFacebook)
yield return new ClassDescriptorKeyValue("Facebook", typeof(string), x => x?.Contacts["Facebook"]);
}
///
///Source Data Class for example
/// of cause you can use any other class
public class PersonalData
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Country { get; set; }
public int Age { get; set; }
public Dictionary<string, string> Contacts { get; set; }
}
}
}
It is also very simple to use DynamicTypeBuilder, you just need put few lines like this:
public class ExampleOfUse
{
private readonly bool includeAddress;
private readonly bool includeFacebook;
private readonly dynamic dynamicType;
private readonly List<ClassDescriptorKeyValue> classDiscriptionList;
public ExampleOfUse(bool includeAddress = false, bool includeFacebook = false)
{
this.includeAddress = includeAddress;
this.includeFacebook = includeFacebook;
this.classDiscriptionList = DynamicTypeBuilderTest.GetAnonymousClassDescription(includeAddress, includeFacebook).ToList();
this.dynamicType = DynamicTypeBuilderTest.CreateAnonymousDynamicType("VeryPrivateData", this.classDiscriptionList);
}
public object Map(PersonalData privateInfo)
{
object dynamicObject = DynamicTypeBuilderTest.CreateAnonymousDynamicInstance(privateInfo, this.dynamicType, classDiscriptionList);
return dynamicObject;
}
}
I hope that this code snippet help somebody =) Enjoy!

Related

Call Select Method on List created using Expression and use a passed Func<> within the Select method

I simply want to do this:
(inputObj ) => (inputObj .Select(objEln=> hubObjectConverter(objEln)));
inputObj ----> List<elnObject>
hubObjectConverter ----> Func<object,object>
Where am I going wrong?
var typeElnObjList = typeof(List<>).MakeGenericType(new[] { elnObjectType });
var inputObj = Expression.Parameter(typeElnObjList, "lstElnObj");
var paramSelectMeth = Expression.Parameter(elnObjectType, "objEln");
var convertToObject = Expression.Invoke(Expression.Constant(hubObjectConverter), paramSelectMeth);
var lambdaSelect = Expression.Lambda(convertToObject, paramSelectMeth);
var convertList = Expression.Call(typeof(Enumerable),
"Select",
new[] { elnObjectType, hubObjectType },
inputObj,
lambdaSelect); <------ I keep getting an error here. Saying Select cannot accept generic type. Where am I going wrong?
(I assume your question is an X/Y problem, so I won't answer your question at face-value)
If your intent is to allow converting from a List<TIn> to a List<TOut> by specifying the type of TOut at runtime with a Type rather than a generic-type parameter then you only need MakeGenericMethod and you don't need to use Expression<> at all:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public static class ListExtensions
{
public static IList ConvertList<TSource>( this List<TSource> source, Type destinationType, Func<object,object> hubObjectConverter )
{
if( source is null ) throw new ArgumentNullException(nameof(source));
if( destinationType is null ) throw new ArgumentNullException(nameof(destinationType));
//
MethodInfo mi = typeof(ListExtensions)
.GetMethod( nameof(ConvertListImpl), BindingFlags.Static | BindingFlags.NonPublic )
.MakeGenericMethod( typeof(TSource), destinationType );
Object result = mi.Invoke( obj: null, new Object[] { source, hubObjectConverter } );
return (IList)result;
}
private static List<TOut> ConvertListImpl<TIn,TOut>( List<TIn> source, Func<Object,Object> converter )
{
return source
.Select( item => converter( item ) )
.Cast<TOut>()
.ToList();
}
}
(To improve performance, the MethodInfo could be cached in a static readonly ConcurrentDictionary<(TIn,TOut),MethodInfo> dictionary).
It would be used like so:
List<Int32> listOfInt32 = new List<Int32>() { 1, 2, 3, 4, 5 };
IList listOfString = listOfInt32.ConvertList( destinationType: typeof(String), obj => obj.ToString() );
Even though listOfString is statically-typed as IList, its actual runtime type is List<String>.
If do know source type ,That is anonymous type . So convert that to object type
class Address
{
public string Street { get; set; }
public Foo Foo { get; set; }
}
class User
{
public string Name { get; set; }
public Address Address { get; set; }
}
public static List<object> ConvertToListOfObjects<T>(List<T> list)
{
return list.ConvertAll<object>(t => t);
}
static void Converttotarget()
{
User testUser = new User()
{
Name = "Paul",
Address = new Address()
{
Street = "Freeway",
Foo = new Foo() { Bar = "Baz" }
}
};
var users = new List<User>();
users.Add(testUser);
var objusers = ConvertToListOfObjects(users);
var testbj = new { name = "test", rol = 1 };
var objelist = new List<object>();
objelist.Add(testbj);
var lst = ConvertToListOfObjects(objelist);
}

Converting a reflection method to a compiled lambda

I'm new to lambda. So excuse me if my question is simple.
I have a method that uses reflection to set a property on some types:
public void WriteId(object obj, int id) {
var type = obj.GetType();
var prop = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead && p.CanWrite)
.Where(p => p.Name == "Id")
.Where(p.PropertyType == typeof(int))
.FirstOrDefault();
if(prop != null)
prop.SetValue(obj, id, null);
}
Can you show me please how can I create a lambda that do same job? Actually I want to create a lambda for each type, compile it, and cache it. Thanks in advance.
I would install FastMember from NuGet, and then use:
var wrapped = ObjectAccessor.Create(obj);
obj["Id"] = id;
which does pretty much what you say, except it happens to use ILGenerator via TypeBuilder (rather than Expression) - but all the caching etc is there.
A second cheaky approach is to get dynamic to do it all for you:
((dynamic)obj).Id = id;
But if you want to use Expression for other reasons:
using System;
using System.Linq.Expressions;
static class Program
{
static void Main()
{
var obj = new Foo { Id = 2 };
WriteId(obj, 6);
Console.WriteLine(obj.Id); // 6
}
private static class SneakyCache<T>
{
public static readonly Action<T, int> SetId;
static SneakyCache()
{
var obj = Expression.Parameter(typeof(T), "obj");
var id = Expression.Parameter(typeof(int), "id");
SetId = Expression.Lambda<Action<T, int>>(
Expression.Assign(Expression.Property(obj, "Id"), id),
obj, id).Compile();
}
}
public static void WriteId<T>(T obj, int id) where T : class
{
SneakyCache<T>.SetId(obj, id);
}
}
class Foo
{
public int Id { get; set; }
}

How do you give a C# Auto-Property a default value using a custom attribute?

How do you give a C# Auto-Property a default value, using a custom attribute?
This is the code I want to see:
class Person
{
[MyDefault("William")]
public string Name { get; set; }
}
I am aware that there is no built in method to initialize the default using an attribute - can I write my own custom class that uses my custom attributes to initialize the default?
If you want to do it with PostSharp (as your tags suggest) then use a Lazy Loading aspect. You can see the one I built here http://programmersunlimited.wordpress.com/2011/03/23/postsharp-weaving-community-vs-professional-reasons-to-get-a-professional-license/
With an aspect you can apply default value to a single property or apply it to multiple properties with a single declaration at the class level.
Lazy loading aspect will use LocationInterceptionAspect base class.
[Serializable]
[LazyLoadingAspect(AttributeExclude=true)]
[MulticastAttributeUsage(MulticastTargets.Property)]
public class LazyLoadingAspectAttribute : LocationInterceptionAspect
{
public object DefaultValue {get; set;}
public override void OnGetValue(LocationInterceptionArgs args)
{
args.ProceedGetValue();
if (args.Value != null)
{
return;
}
args.Value = DefaultValue;
args.ProceedSetValue();
}
}
then apply the aspect like so
[LazyLoadingAspect(DefaultValue="SomeValue")]
public string MyProp { get; set; }
You could use a helper class like that:
public class DefaultValueHelper
{
public static void InitializeDefaultValues<T>(T obj)
{
var properties =
(from prop in obj.GetType().GetProperties()
let attr = GetDefaultValueAttribute(prop)
where attr != null
select new
{
Property = prop,
DefaultValue = attr.Value
}).ToArray();
foreach (var p in properties)
{
p.Property.SetValue(obj, p.DefaultValue, null);
}
}
private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.FirstOrDefault();
}
}
And call InitializeDefaultValues in the constructor of your class.
class Foo
{
public Foo()
{
DefaultValueHelper.InitializeDefaultValues(this);
}
[DefaultValue("(no name)")]
public string Name { get; set; }
}
EDIT: updated version, which generates and caches a delegate to do the initialization. This is to avoid using reflection every time the method is called for a given type.
public static class DefaultValueHelper
{
private static readonly Dictionary<Type, Action<object>> _initializerCache;
static DefaultValueHelper()
{
_initializerCache = new Dictionary<Type, Action<object>>();
}
public static void InitializeDefaultValues(object obj)
{
if (obj == null)
return;
var type = obj.GetType();
Action<object> initializer;
if (!_initializerCache.TryGetValue(type, out initializer))
{
initializer = MakeInitializer(type);
_initializerCache[type] = initializer;
}
initializer(obj);
}
private static Action<object> MakeInitializer(Type type)
{
var arg = Expression.Parameter(typeof(object), "arg");
var variable = Expression.Variable(type, "x");
var cast = Expression.Assign(variable, Expression.Convert(arg, type));
var assignments =
from prop in type.GetProperties()
let attr = GetDefaultValueAttribute(prop)
where attr != null
select Expression.Assign(Expression.Property(variable, prop), Expression.Constant(attr.Value));
var body = Expression.Block(
new ParameterExpression[] { variable },
new Expression[] { cast }.Concat(assignments));
var expr = Expression.Lambda<Action<object>>(body, arg);
return expr.Compile();
}
private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.FirstOrDefault();
}
}
If to speculate with Expressions you could make initializing delegates and cache them. It will make code much faster comparing with just pure reflection.
internal static class Initializer
{
private class InitCacheEntry
{
private Action<object, object>[] _setters;
private object[] _values;
public InitCacheEntry(IEnumerable<Action<object, object>> setters, IEnumerable<object> values)
{
_setters = setters.ToArray();
_values = values.ToArray();
if (_setters.Length != _values.Length)
throw new ArgumentException();
}
public void Init(object obj)
{
for (int i = 0; i < _setters.Length; i++)
{
_setters[i](obj, _values[i]);
}
}
}
private static Dictionary<Type, InitCacheEntry> _cache = new Dictionary<Type, InitCacheEntry>();
private static InitCacheEntry MakeCacheEntry(Type targetType)
{
var setters = new List<Action<object, object>>();
var values = new List<object>();
foreach (var propertyInfo in targetType.GetProperties())
{
var attr = (DefaultAttribute) propertyInfo.GetCustomAttributes(typeof (DefaultAttribute), true).FirstOrDefault();
if (attr == null) continue;
var setter = propertyInfo.GetSetMethod();
if (setter == null) continue;
// we have to create expression like (target, value) => ((TObj)target).setter((T)value)
// where T is the type of property and obj is instance being initialized
var targetParam = Expression.Parameter(typeof (object), "target");
var valueParam = Expression.Parameter(typeof (object), "value");
var expr = Expression.Lambda<Action<object, object>>(
Expression.Call(Expression.Convert(targetParam, targetType),
setter,
Expression.Convert(valueParam, propertyInfo.PropertyType)),
targetParam, valueParam);
var set = expr.Compile();
setters.Add(set);
values.Add(attr.DefaultValue);
}
return new InitCacheEntry(setters, values);
}
public static void Init(object obj)
{
Type targetType = obj.GetType();
InitCacheEntry init;
if (!_cache.TryGetValue(targetType, out init))
{
init = MakeCacheEntry(targetType);
_cache[targetType] = init;
}
init.Init(obj);
}
}
You could create a method like this:
public static void FillProperties<T>(T obj)
{
foreach (var property in typeof(T).GetProperties())
{
var attribute = property
.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.SingleOrDefault();
if (attribute != null)
property.SetValue(obj, attribute.Value, null);
}
}
You can then either use a factory method that calls this method or call it directly from the constructor. Note that this usage of reflection is probably not a good idea if you create a lot of objects this way and performance is important.

How can I avoid using a string member name to get a member of an anonymous type?

I'm using the following code to retrieve named members from an anonymous type. Is there some way I could convert the follwing code to use a lambda expression to achieve this, or at least to allow the calling code to use a lamda, even if 'deep down' I have to use a string?
private T GetAnonymousTypeMember<T>(object anonymousType, string memberName) where T: class
{
var anonTypesType = anonymousType.GetType();
var propInfo = anonTypesType.GetProperty(memberName);
return propInfo.GetValue(anonymousType, null) as T;
}
ADDED:
This is how anonymousType arrives. The GetAnonymousTypeMember method is private to a class whose only public method is declared as follows:
public void PublishNotification(NotificationTriggers trigger, object templateParameters)
I call this method:
PublishNotification(NotificationTriggers.RequestCreated, new {NewJobCard = model});
That new {NewJobCard = model} is what is passed to GetAnonymousTypeMember as anonymousType.
public U GetMemberValue<T, U>(T instance, Expression<Func<T, U>> selector)
{
Type type = typeof(T);
var expr = selector.Body as MemberExpression;
string name = expr.Member.Name;
var prop = type.GetProperty(name);
return (U)prop.GetValue(instance, null);
}
Will enable to to do:
string name = GetMemberValue(new { Name = "Hello" }, o => o.Name);
But why don't You just use dynamic? eg:
class MyClass
{
public int member = 123;
}
class Program
{
static void Main(string[] args)
{
MyClass obj = new MyClass();
dynamic dynObj = obj;
Console.WriteLine(dynObj.member);
Console.ReadKey();
}
}
You could also involve ExpandoObject
List<dynamic> objs = new List<dynamic>();
dynamic objA = new ExpandoObject();
objA.member = "marian";
objs.Add(objA);
dynamic objB = new ExpandoObject();
objB.member = 123;
objs.Add(objB);
dynamic objC = new ExpandoObject();
objC.member = Guid.NewGuid();
objs.Add(objC);
foreach (dynamic obj in objs)
Console.WriteLine(obj.member);
Console.ReadKey();
You mean something like this?
private R GetAnonymousTypeMember<T, R>(T anonymousType, Expression<Func<T, R>> e) where T : class
{
return e.Compile()(anonymousType);
}
public void Do()
{
var x = new {S = "1", V = 2};
var v = GetAnonymousTypeMember(x, _ => _.V);
}
No, when you send the object as the type object, there is no specific type information for the parameter. If you want to access the members of the object you need to use reflection to get the type information from the object itself.
Using a lambda expression to get the member would work, but that would be utterly pointless...

Expression to Call a Method on Each Property of a Class

I want to take a class, loop through it's properties, get the property value, and call a method passing that property value in. I think I can get the property values, but what does the lambda expression's body look like? What body is used to call a method on each property?
This is what I have so far...
Action<T> CreateExpression<T>( T obj )
{
foreach( var property in typeof( T ).GetProperties() )
{
Expression value = Expression.Property( Expression.Constant( obj ), property );
var method = Expression.Call( typeof( SomeType ), "SomeMethod", null, value );
}
// What expression body can be used that will call
// all the method expressions for each property?
var body = Expression...
return Expression.Lambda<Action<T>>( body, ... ).Compile();
}
It depends on a few things.
does the method return anything? Expression in 3.5 can't do multiple separate "action" operations (a statement body), but you can cheat if you can do something with a fluent API:
SomeMethod(obj.Prop1).SomeMethod(obj.Prop2).SomeMethod(obj.Prop3);
(perhaps using generics to make it simpler)
do you have access to 4.0? In 4.0 there are additional Expression types allowing statement bodies and exactly what you ask for. I discuss some similar examples in an article here (look for Expression.Block, although this is based on a beta a while ago - it may have been renamed by now).
Alternative; since you are compiling to a delegate, consider that an Action<T> is multicast; you could build a set of simple operations, and combine them in the delegate; this would work in 3.5; for example:
using System;
using System.Linq.Expressions;
static class SomeType
{
static void SomeMethod<T>(T value)
{
Console.WriteLine(value);
}
}
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
static class Program
{
static readonly Action<Customer> action = CreateAction<Customer>();
static void Main()
{
Customer cust = new Customer { Id = 123, Name = "Abc" };
action(cust);
}
static Action<T> CreateAction<T>()
{
Action<T> result = null;
var param = Expression.Parameter(typeof(T), "obj");
foreach (var property in typeof(T).GetProperties(
BindingFlags.Instance | BindingFlags.Public))
{
if (property.GetIndexParameters().Length > 0) continue;
var propVal = Expression.Property(param, property);
var call = Expression.Call(typeof(SomeType), "SomeMethod", new Type[] {propVal.Type}, propVal);
result += Expression.Lambda<Action<T>>(call, param).Compile();
}
return result;
}
}
I dont think it will be so easy using Expressions, in .NET 3.5 at least.
.NET 4 supports a block construct I believe.
I suggest using Reflection.Emit rather.
Here is a starting point (for fields but can be changed easily):
internal static T CreateDelegate<T>(this DynamicMethod dm) where T : class
{
return dm.CreateDelegate(typeof(T)) as T;
}
static Dictionary<Type, Func<object, Dictionary<string, object>>> fieldcache =
new Dictionary<Type, Func<object, Dictionary<string, object>>>();
static Dictionary<string, object> GetFields(object o)
{
var t = o.GetType();
Func<object, Dictionary<string, object>> getter;
if (!fieldcache.TryGetValue(t, out getter))
{
var rettype = typeof(Dictionary<string, object>);
var dm = new DynamicMethod(t.Name + ":GetFields",
rettype, new Type[] { typeof(object) }, t);
var ilgen = dm.GetILGenerator();
var instance = ilgen.DeclareLocal(t);
var dict = ilgen.DeclareLocal(rettype);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Castclass, t);
ilgen.Emit(OpCodes.Stloc, instance);
ilgen.Emit(OpCodes.Newobj, rettype.GetConstructor(Type.EmptyTypes));
ilgen.Emit(OpCodes.Stloc, dict);
var add = rettype.GetMethod("Add");
foreach (var field in t.GetFields(
BindingFlags.DeclaredOnly |
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic))
{
if (!field.FieldType.IsSubclassOf(typeof(Component)))
{
continue;
}
ilgen.Emit(OpCodes.Ldloc, dict);
ilgen.Emit(OpCodes.Ldstr, field.Name);
ilgen.Emit(OpCodes.Ldloc, instance);
ilgen.Emit(OpCodes.Ldfld, field);
ilgen.Emit(OpCodes.Castclass, typeof(object));
ilgen.Emit(OpCodes.Callvirt, add);
}
ilgen.Emit(OpCodes.Ldloc, dict);
ilgen.Emit(OpCodes.Ret);
fieldcache[t] = getter = dm.CreateDelegate<Func<object,
Dictionary<string, object>>>();
}
return getter(o);
}
Use the Block statement. The code below for example writes out the names of all properties
static void WritePropertyNames()
{
TestObject lTestObject = new TestObject();
PropertyInfo[] lProperty = typeof(TestObject).GetProperties();
List<Expression> lExpressions = new List<Expression>();
MethodInfo lMethodInfo = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
lProperty.ForEach(x =>
{
ConstantExpression lConstant = Expression.Constant(x.Name);
MethodCallExpression lMethodCall = Expression.Call(lMethodInfo, lConstant);
lExpressions.Add(lMethodCall);
});
BlockExpression lBlock = Expression.Block(lExpressions);
LambdaExpression lLambda = Expression.Lambda<Action>(lBlock, null);
Action lWriteProperties = lLambda.Compile() as Action;
lWriteProperties();
}
Expression trees can only contain a single statement. To do what you are trying you would need to Expression.Lambda<>() in your loop, passing "method" as the body.
I believe this has changed in .NET Framework 4.0.
Andrew
If you're willing to have your method SomeType.SomeMethod accept an object[] then you can do something like this (note that indexers can not be handled here so we discard them):
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Test {
class SomeType {
public static void SomeMethod(object[] values) {
foreach (var value in values) {
Console.WriteLine(value);
}
}
}
class Program {
static Action<T> CreateAction<T>() {
ParameterExpression parameter = Expression.Parameter(
typeof(T),
"parameter"
);
List<Expression> properties = new List<Expression>();
foreach (var info in typeof(T).GetProperties()) {
// can not handle indexers
if(info.GetIndexParameters().Length == 0) {
Expression property = Expression.Property(parameter, info);
properties.Add(Expression.Convert(property, typeof(object)));
}
}
Expression call = Expression.Call(
typeof(SomeType).GetMethod("SomeMethod"),
Expression.NewArrayInit(typeof(object), properties)
);
return Expression.Lambda<Action<T>>(call, parameter).Compile();
}
static void Main(string[] args) {
Customer c = new Customer();
c.Name = "Alice";
c.ID = 1;
CreateAction<Customer>()(c);
}
}
class Customer {
public string Name { get; set; }
public int ID { get; set; }
}
}
Of course this will be easier in .NET 4.0 with the LoopExpression.

Categories