I need to trim some string properties in my objects, but I don't want to go to all objects and properties and in the set properties do the trim method (there is a lot of objects, 300+ and a lot of string properties).
One tip: all my objects have a super class called CoreTransaction, so I can use it (with some kind of reflection) to do this thing more easily.
Is that possible?
var stringProperties = obj.GetType().GetProperties()
.Where(p => p.PropertyType == typeof (string));
foreach (var stringProperty in stringProperties)
{
string currentValue = (string) stringProperty.GetValue(obj, null);
stringProperty.SetValue(obj, currentValue.Trim(), null) ;
}
Thank you to Bala R for your solution to the OP's problem. I converted your solution to an extension method and fixed a problem where null values were throwing errors.
/// <summary>Trim all String properties of the given object</summary>
public static TSelf TrimStringProperties<TSelf>(this TSelf input)
{
var stringProperties = input.GetType().GetProperties()
.Where(p => p.PropertyType == typeof(string) && p.CanWrite);
foreach (var stringProperty in stringProperties)
{
string currentValue = (string)stringProperty.GetValue(input, null);
if (currentValue != null)
stringProperty.SetValue(input, currentValue.Trim(), null);
}
return input;
}
I fixed landi's answer to accommodate child nullable objects and handle IEnumerable collections (loop through a List of object and trim string properties). I made an edit to his answer which was rejected for not being on topic, but that's a load of garbage. Hopefully this helps someone, as landi's answer didn't work on every object type I had. Now it does.
public static class ExtensionMethods
{
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
if(obj != null)
{
if(obj is IEnumerable)
{
foreach(var listItem in obj as IEnumerable)
{
listItem.TrimAllStrings();
}
}
else
{
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo p in obj.GetType().GetProperties(flags))
{
Type currentNodeType = p.PropertyType;
if (currentNodeType == typeof (String))
{
string currentValue = (string)p.GetValue(obj, null);
if (currentValue != null)
{
p.SetValue(obj, currentValue.Trim(), null);
}
}
// see http://stackoverflow.com/questions/4444908/detecting-native-objects-with-reflection
else if (currentNodeType != typeof (object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
p.GetValue(obj, null).TrimAllStrings();
}
}
}
}
}
}
I have written a Extension Method which also takes care of subclasses and strings on referenced classes (like parent.Child.Name)
public static class ExtensionMethods
{
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo p in obj.GetType().GetProperties(flags))
{
Type currentNodeType = p.PropertyType;
if (currentNodeType == typeof (String))
{
string currentValue = (string)p.GetValue(obj, null);
if (currentValue != null)
{
p.SetValue(obj, currentValue.Trim(), null);
}
}
// see http://stackoverflow.com/questions/4444908/detecting-native-objects-with-reflection
else if (currentNodeType != typeof (object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
p.GetValue(obj, null).TrimAllStrings();
}
}
}
}
I'm not sure about changing the behaviour of your accessors. That doesn't sound easy at all. How about adding the trimming to your base class?
class CoreTransaction
{
public void Trim()
{
IEnumerable<PropertyInfo> stringProperties =
this.GetType().GetProperties()
.Where(p => p.PropertyType == typeof(string) && p.CanRead && p.CanWrite);
foreach (PropertyInfo property in stringProperties)
{
string value = (string)property.GetValue(this, null);
value = value.Trim();
property.SetValue(this, value, null);
}
}
}
(Also, note the check that your fields can be both read and written to.)
EDIT: You could then add something like this to your base class, and trim all of them in one go.
The WeakReference class will allow you to easily keep track of your instances without getting in the way of the garbage collector:
class CoreTransaction
{
private static List<WeakReference> allCoreTransactions = new List<WeakReference>();
public CoreTransaction()
{
allCoreTransactions.Add(new WeakReference(this));
}
public static void TrimAll()
{
foreach (WeakReference reference in allCoreTransactions)
{
if (reference.IsAlive)
{
((CoreTransaction)reference.Target).Trim();
}
}
}
}
You could use reflection to do something like this:
// o is your instance object
List<PropertyInfo> fields = o.GetType().GetProperties()
.Where(i => i.PropertyType == typeof(string));
fields.ForEach(i => i.SetValue(o, ((string)i.GetValue(o, null)).Trim(), new object[]{}));
Thank landi for his solution. I altered his method to add support for Classes with Index Parameters and also to check if the obj is null before continuing.
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
if (obj == null)
return;
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo p in obj.GetType().GetProperties(flags))
{
Type currentNodeType = p.PropertyType;
if (currentNodeType == typeof(String))
{
string currentValue = (string)p.GetValue(obj, null);
if (currentValue != null)
{
p.SetValue(obj, currentValue.Trim(), null);
}
}
// see http://stackoverflow.com/questions/4444908/detecting-native-objects-with-reflection
else if (currentNodeType != typeof(object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
if (p.GetIndexParameters().Length == 0)
{
p.GetValue(obj, null).TrimAllStrings();
}else
{
p.GetValue(obj, new Object[] { 0 }).TrimAllStrings();
}
}
}
}
Extended Own's solution and added a check that it's possible to write to property. Had some "Property set method not found" errors due to a Uri property.
public static class ExtensionMethods
{
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
if(obj != null)
{
if(obj is IEnumerable)
{
foreach(var listItem in obj as IEnumerable)
{
listItem.TrimAllStrings();
}
}
else
{
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo p in obj.GetType().GetProperties(flags))
{
Type currentNodeType = p.PropertyType;
if (currentNodeType == typeof (String))
{
string currentValue = (string)p.GetValue(obj, null);
if (currentValue != null && p.CanWrite)
{
p.SetValue(obj, currentValue.Trim(), null);
}
}
// see http://stackoverflow.com/questions/4444908/detecting-native-objects-with-reflection
else if (currentNodeType != typeof (object) && Type.GetTypeCode(currentNodeType) == TypeCode.Object)
{
p.GetValue(obj, null).TrimAllStrings();
}
}
}
}
}
}
For those who use VB.NET, I converted thrawnis's answer and added a condition to only return those properties that are not ReadOnly. Otherwise if your class has readonly properties you will get runtime error when you try to SetValue for those properties.
''' <summary>
''' Trim all NOT ReadOnly String properties of the given object
''' </summary>
<Extension()>
Public Function TrimStringProperties(Of T)(ByVal input As T) As T
Dim stringProperties = input.GetType().GetProperties().Where(Function(p) p.PropertyType = GetType(String) AndAlso p.CanWrite)
For Each stringProperty In stringProperties
Dim currentValue As String = Convert.ToString(stringProperty.GetValue(input, Nothing))
If currentValue IsNot Nothing Then
stringProperty.SetValue(input, currentValue.Trim(), Nothing)
End If
Next
Return input
End Function
You can try it:
static public class Trim<T>
where T : class
{
static public readonly Action<T> TrimAllStringFields = Trim<T>.CreateTrimAllStringFields();
static private Action<T> CreatTrimAllStringFields()
{
var instance = Expression.Parameter(typeof(T));
return Expression.Lambda<Action<T>>(Expression.Block(instance.Type.GetFields(BindingsFlags.Instance| BindingFlags.NonPublic | BindingFlags.Public).Select(field => Expression.Assign(Expression.Field(instance, field)) as Expression), instance).Compile();
}
}
Use it like this :
var myinstance = new MyClass();
Trim<MyClass>.TrimAllStringFields(myinstance);
I took OwN's answer but made these changes:
used early exit to decrease the nesting of if
used var everywhere, renaming a few variables
added Unit Tests
ObjectExtensions.cs
using System;
using System.Collections;
using System.Reflection;
namespace YourProject.Infrastructure.Extensions
{
public static class ObjectExtensions
{
// Derived from https://stackoverflow.com/a/50193184/
public static void TrimAllStrings<TSelf>(this TSelf obj)
{
if (obj == null)
{
return;
}
if (obj is IEnumerable)
{
foreach (var item in obj as IEnumerable)
{
item.TrimAllStrings();
}
return;
}
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (var prop in obj.GetType().GetProperties(flags))
{
var nodeType = prop.PropertyType;
if (nodeType == typeof(String))
{
string currentValue = (string)prop.GetValue(obj, null);
if (currentValue != null)
{
prop.SetValue(obj, currentValue.Trim(), null);
}
}
// see http://stackoverflow.com/questions/4444908/detecting-native-objects-with-reflection
else if (nodeType != typeof(object) && Type.GetTypeCode(nodeType) == TypeCode.Object)
{
prop.GetValue(obj, null).TrimAllStrings();
}
}
}
}
}
ObjectExtensionsTests.cs
using System.Collections.Generic;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourProject.Infrastructure.Extensions;
namespace YourProjectTests.Infrastructure.Extensions
{
[TestClass]
public class ObjectExtensionsTests
{
[TestMethod]
public void NullObject_DoesNothing()
{
// Arrange
SomeStringPropertiesClass test = null;
// Act
test.TrimAllStrings();
}
public class NoStringPropertiesClass
{
public int IntProperty { get; set; }
}
[TestMethod]
public void NoStringProperties_DoesNothing()
{
// Arrange
var test = new NoStringPropertiesClass()
{
IntProperty = 42
};
// Act
test.TrimAllStrings();
// Assert
test.IntProperty.Should().Be(42);
}
public class SomeStringPropertiesClass
{
public int IntProperty { get; set; }
public string StringProperty1 { get; set; }
public string StringProperty2 { get; set; }
public string StringProperty3 { get; set; }
public List<SomeStringPropertiesClass> Children { get; set; } = new();
}
[TestMethod]
public void SomeStringProperties_DoesTrimStrings()
{
// Arrange
var test = new SomeStringPropertiesClass()
{
IntProperty = 42,
StringProperty1 = "Already trimmed string",
StringProperty2 = " Needs trimming ",
StringProperty3 = "",
Children = new()
{
new SomeStringPropertiesClass()
{
StringProperty1 = " Child that needs trimming ",
StringProperty2 = null,
StringProperty3 = " Child that needs trimming . ",
Children = new()
{
null,
new SomeStringPropertiesClass()
{
StringProperty2 = " Grandchild that needs trimming ",
},
null
}
}
}
};
// Act
test.TrimAllStrings();
// Assert
test.IntProperty.Should().Be(42);
test.StringProperty1.Should().Be("Already trimmed string");
test.StringProperty2.Should().Be("Needs trimming");
test.StringProperty3.Should().BeEmpty();
test.Children[0].StringProperty1.Should().Be("Child that needs trimming");
test.Children[0].StringProperty2.Should().BeNull();
test.Children[0].StringProperty3.Should().Be("Child that needs trimming .");
test.Children[0].Children[1].StringProperty1.Should().BeNull();
test.Children[0].Children[1].StringProperty2.Should().Be("Grandchild that needs trimming");
test.Children[0].Children[1].StringProperty3.Should().BeNull();
}
}
}
Thanks #Teter28 for the idea with code generation. It's much more effective than solutions with reflection.
The provided code example does not work. Here's ready for use example.
public static class Trimmer<T>
{
private static readonly Action<T> TrimAllStringFieldsAction = CreateTrimAllStringPropertiesMethod();
public static void TrimAllStringProperties(T parameter)
{
TrimAllStringFieldsAction(parameter);
}
private static Action<T> CreateTrimAllStringPropertiesMethod()
{
var parameter = Expression.Parameter(typeof(T));
var trimMethod = typeof(string).GetMethod(nameof(string.Trim), Type.EmptyTypes);
return Expression.Lambda<Action<T>>(
Expression.Block(
parameter.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(propertyInfo => propertyInfo.PropertyType == typeof(string))
.Select(propertyInfo => Expression.Assign(
Expression.Property(parameter, propertyInfo),
Expression.Call(Expression.Property(parameter, propertyInfo), trimMethod!)))),
parameter)
.Compile();
}
}
Related
I have the following extension method which fills in missing values of a class member from a source object of the same class. The missing part of this method is when a member of the class is also a class object of another class that also has members of its own, so I need a recursion here, but I didn't find any way to get the inner members of the class member and being able to pass it back to the FillValues method...
For example, I have a class called User and a class called UserLogin like this:
public class User
{
public string FirstName;
public string LastName;
public UserLogin Login;
}
public class UserLogin
{
public string Email;
public string Password;
}
When I call FillValues on a member of class User if fills in the missing fields from FirstName and LastName but not from Login because its a class member also.
How can I pass the member Login recursively so that nested members will also be filled with values?
public static void FillValues<T>(this T target, T source)
{
FillMissingProperties(target, source);
FillMissingFields(target, source);
}
private static void FillMissingProperties<T>(T target, T source)
{
var properties = typeof(T).GetProperties().Where(prop => prop.CanRead && prop.CanWrite);
foreach (var prop in properties)
{
var targetValue = prop.GetValue(target, null);
var defaultValue = prop.PropertyType.GetTypeInfo().IsValueType ? Activator.CreateInstance(prop.PropertyType) : null;
if (targetValue == null || targetValue.Equals(defaultValue))
{
var sourceValue = prop.GetValue(source, null);
prop.SetValue(target, sourceValue, null);
}
}
}
private static void FillMissingFields<T>(T target, T source)
{
var fields = typeof(T).GetFields();
foreach (var field in fields)
{
var targetValue = field.GetValue(target);
var sourceValue = field.GetValue(source);
var defaultValue = field.FieldType.GetTypeInfo().IsValueType ? Activator.CreateInstance(field.FieldType) : null;
if (targetValue == null || targetValue.Equals(defaultValue))
field.SetValue(target, sourceValue);
}
}
You need to recursively call FillValues for the class fields/properties that are of a class type. To do this you need to have a non generic version of this method:
public static void FillValues<T>(this T target, T source)
{
FillValues(typeof(T), target, source);
}
private static void FillValues(Type type, object target, object source)
{
FillMissingProperties(type, target, source);
FillMissingFields(type, target, source);
}
private static void FillMissingProperties(Type type, object target, object source)
{
var properties = type.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);
foreach (var prop in properties)
{
var targetValue = prop.GetValue(target, null);
var defaultValue = prop.PropertyType.GetTypeInfo().IsValueType ? Activator.CreateInstance(prop.PropertyType) : null;
if (targetValue == null || targetValue.Equals(defaultValue))
{
var sourceValue = prop.GetValue(source, null);
prop.SetValue(target, sourceValue, null);
}
else if (targetValue != null && prop.PropertyType != typeof(string) && prop.PropertyType.GetTypeInfo().IsClass)
{
var sourceValue = prop.GetValue(source, null);
FillValues(prop.PropertyType, targetValue, sourceValue);
}
}
}
private static void FillMissingFields(Type type, object target, object source)
{
var fields = type.GetFields();
foreach (var field in fields)
{
var targetValue = field.GetValue(target);
var sourceValue = field.GetValue(source);
var defaultValue = field.FieldType.GetTypeInfo().IsValueType ? Activator.CreateInstance(field.FieldType) : null;
if (targetValue == null || targetValue.Equals(defaultValue))
{
field.SetValue(target, sourceValue);
}
else if(targetValue != null && field.FieldType != typeof(string) && field.FieldType.GetTypeInfo().IsClass)
{
FillValues(field.FieldType, targetValue, sourceValue);
}
}
}
I have a need to update object A's property if null with that from object B's equivalent property if that is not null. I wanted code I can use for various objects.
I had a version working until one of the objects contained a propert of type List, which is where I have a blank in the code below. My main question is how can I best implement this part of the code. Secondly is there a better way of doing this whole thing and thirdly I know its never going to be rapid but any suggestions to speed it up would be appreciated.
Thanks in advance.
public T MergeWith<T, U>(T primarySource, U secondarySource) where U : class, T
{
Type primaryType = typeof(T);
Type secondaryType = typeof(U);
foreach (PropertyInfo primaryInfo in primaryType.GetProperties())
{
if (primaryInfo.CanWrite)
{
object currentPrimary = primaryInfo.GetValue(primarySource, null);
PropertyInfo secondaryInfo = secondaryType.GetProperty(primaryInfo.Name);
object currentSecondary = secondaryInfo.GetValue(secondarySource, null);
if (currentPrimary == null && currentSecondary != null)
{
primaryInfo.SetValue(primarySource, currentSecondary, null);
}
else if ((currentPrimary != null && currentSecondary != null) && isChildClass(primaryInfo))
{
if (isCollection(currentPrimary))
{
// here
}
else
{
MethodInfo method = typeof(NavigationModel).GetMethod("MergeWith");
MethodInfo generic = method.MakeGenericMethod(primaryInfo.PropertyType, primaryInfo.PropertyType);
object returnChild = generic.Invoke(this, new object[2] { currentPrimary, currentSecondary });
}
}
}
}
return primarySource;
}
private bool isCollection(object property)
{
return typeof(ICollection).IsAssignableFrom(property.GetType())
|| typeof(ICollection<>).IsAssignableFrom(property.GetType());
}
private bool isChildClass(PropertyInfo propertyInfo)
{
return (propertyInfo.PropertyType.IsClass && !propertyInfo.PropertyType.IsValueType &&
!propertyInfo.PropertyType.IsPrimitive && propertyInfo.PropertyType.FullName != "System.String");
}
I have created the below extension method for use in my latest project and it works fine, collections and all. It is a pretty much a simpler version of what you are doing in your method. With mine both classes have to be the same type. What problem do you encounter with collections?
public static class ExtensionMethods
{
public static TEntity CopyTo<TEntity>(this TEntity OriginalEntity, TEntity NewEntity)
{
PropertyInfo[] oProperties = OriginalEntity.GetType().GetProperties();
foreach (PropertyInfo CurrentProperty in oProperties.Where(p => p.CanWrite))
{
if (CurrentProperty.GetValue(NewEntity, null) != null)
{
CurrentProperty.SetValue(OriginalEntity, CurrentProperty.GetValue(NewEntity, null), null);
}
}
return OriginalEntity;
}
}
Hi I modified Ben Robinsons solution in order to not overwrite Collections or list, instead, it adds the elements of one object to the other one where the merging is happening:
public static class ExtensionMethods
{
public static TEntity CopyTo<TEntity>(this TEntity OriginalEntity, TEntity EntityToMergeOn)
{
PropertyInfo[] oProperties = OriginalEntity.GetType().GetProperties();
foreach (PropertyInfo CurrentProperty in oProperties.Where(p => p.CanWrite))
{
var originalValue = CurrentProperty.GetValue(EntityToMergeOn);
if (originalValue != null)
{
IListLogic<TEntity>(OriginalEntity, CurrentProperty, originalValue);
}
else
{
var value = CurrentProperty.GetValue(OriginalEntity, null);
CurrentProperty.SetValue(EntityToMergeOn, value, null);
}
}
return OriginalEntity;
}
private static void IListLogic<TEntity>(TEntity OriginalEntity, PropertyInfo CurrentProperty, object originalValue)
{
if (originalValue is IList)
{
var tempList = (originalValue as IList);
var existingList = CurrentProperty.GetValue(OriginalEntity) as IList;
foreach (var item in tempList)
{
existingList.Add(item);
}
}
}
}
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
I have a class with a few properties, I've got a custom attribute setup, one for TextField and one for ValueField, I am using an IEnumerable, so can't just select the fields I want, I need to basically:
collectionItems.ToDictionary(o => o.FieldWithAttribute<TextField>, o => o.FieldWithAttribute<ValueField>);
Hopefully you get what I am trying to do, this doesn't need to use generics as above, I just need to do something similar to get the marked fields from a large object, so I can have a nice little dictionary of key value pairs.
Example class that is the TEntity:
public class Product
{
[TextField]
public string ProductTitle { get; set; }
[ValueField]
public int StyleID { get; set; }
//Other fields...
}
Any ideas how I can achieve this? Perhaps using reflection somehow in the LINQ statement?
If you're going to use reflection, you should probably cache the member accessors to avoid the performance hit of reflecting on every item, every time. You could do something like this:
// Type aliases used for brevity
using Accessor = System.Func<object, object>;
using E = System.Linq.Expressions.Expression;
internal static class AttributeHelpers
{
private const BindingFlags DeclaredFlags = BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.DeclaredOnly;
private const BindingFlags InheritedFlags = BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic;
private static readonly Accessor NullCallback = _ => null;
[ThreadStatic]
private static Dictionary<Type, Dictionary<Type, Accessor>> _cache;
private static Dictionary<Type, Accessor> GetCache<TAttribute>()
where TAttribute : Attribute
{
if (_cache == null)
_cache = new Dictionary<Type, Dictionary<Type, Accessor>>();
Dictionary<Type, Accessor> cache;
if (_cache.TryGetValue(typeof(TAttribute), out cache))
return cache;
cache = new Dictionary<Type, Accessor>();
_cache[typeof(TAttribute)] = cache;
return cache;
}
public static object MemberWithAttribute<TAttribute>(this object target)
where TAttribute : Attribute
{
if (target == null)
return null;
var accessor = GetAccessor<TAttribute>(target.GetType());
if (accessor != null)
return accessor(target);
return null;
}
private static Accessor GetAccessor<TAttribute>(Type targetType)
where TAttribute : Attribute
{
Accessor accessor;
var cache = GetCache<TAttribute>();
if (cache.TryGetValue(targetType, out accessor))
return accessor;
var member = FindMember<TAttribute>(targetType);
if (member == null)
{
cache[targetType] = NullCallback;
return NullCallback;
}
var targetParameter = E.Parameter(typeof(object), "target");
var accessorExpression = E.Lambda<Accessor>(
E.Convert(
E.MakeMemberAccess(
E.Convert(targetParameter, targetType),
member),
typeof(object)),
targetParameter);
accessor = accessorExpression.Compile();
cache[targetType] = accessor;
return accessor;
}
private static MemberInfo FindMember<TAttribute>(Type targetType)
where TAttribute : Attribute
{
foreach (var property in targetType.GetProperties(DeclaredFlags))
{
var attribute = property.GetCustomAttribute<TAttribute>();
if (attribute != null)
return property;
}
foreach (var field in targetType.GetFields(DeclaredFlags))
{
var attribute = field.GetCustomAttribute<TAttribute>();
if (attribute != null)
return field;
}
foreach (var property in targetType.GetProperties(InheritedFlags))
{
var attribute = property.GetCustomAttribute<TAttribute>();
if (attribute != null)
return property;
}
foreach (var field in targetType.GetFields(InheritedFlags))
{
var attribute = field.GetCustomAttribute<TAttribute>();
if (attribute != null)
return field;
}
return null;
}
}
It's up to you how you want to deal with items whose types lack the desired attributed members. I chose to return null.
Example usage:
var lookup = Enumerable
.Range(1, 20)
.Select(i => new Product { Title = "Product " + i, StyleID = i })
.Select(
o => new
{
Text = o.MemberWithAttribute<TextFieldAttribute>(),
Value = o.MemberWithAttribute<ValueFieldAttribute>()
})
.Where(o => o.Text != null && o.Value != null)
.ToDictionary(o => o.Text, o => o.Value);
foreach (var key in lookup.Keys)
Console.WriteLine("{0}: {1}", key, lookup[key]);
public static object FieldWithAttribute<T>(this object obj)
{
var field = obj.GetType()
.GetProperties()
.SIngleOrDefault(x => x.CustomAattributes.Any(y => y.AttributeType == typeof(T));
return field != null ? field.GetValue(obj) : null;
}
something like this
This should do the trick
public static TRet FieldWithAttribute<TAttr, TRet>(this object obj) where TAttr : Attribute
{
var field = obj.GetType()
.GetProperties()
.SingleOrDefault(x => Attribute.IsDefined(x, typeof (TAttr)));
return field == null ? default(TRet) : (TRet)field.GetValue(obj);
}
and when you use it
var dictionary = products.ToDictionary(x => x.FieldWithAttribute<TextFieldAttribute, string>(),
x => x.FieldWithAttribute<ValueFieldAttribute, int>());
When I run the following code, it only returns a MethodInfo/FieldInfo/etc. that belongs directly to the Type that I'm searching for the info object in. How do I find the info object regardless of the fact that it resides in a base class and could be private?
obj.GetType().GetMethod(methodName, bindingFlags);
Well, you answered your own question, but as far as I understood, you main requirement is How do I find the info object regardless of where it is found in the hierarchy?
You don't need recursion here to get all members in the full hierarchy. You can use GetMembers function on a Type and it will return all members including all base classes.
Next code example demonstrates this:
var names =
typeof(MyClass).GetMembers()
.Select (x => x.Name);
Console.WriteLine (string.Join(Environment.NewLine, names));
for such structure
class MyClass : Base
{
public string Name { get; set; }
public string Surname { get; set; }
}
class Base
{
public string Name { get; set; }
}
returns
get_Name
set_Name
get_Surname
set_Surname
get_Name
set_Name
ToString
Equals
GetHashCode
GetType
.ctor
Name
Surname
note that get_Name accessor for auto-property appears twice because MyClass hides Name property of the base class. Also note ToString, GetType and other methods, defined in object class
The following code will look through each base class of an object if the info object is not found in the child class. Note that though it will return the base class info object, it will return the object that it "runs into first", so if you have a variable called _blah in your child class and a variable called _blah in a base class, then the _blah from the child class will be returned.
public static MethodInfo GetMethodInfo(this Type objType, string methodName, BindingFlags flags, bool isFirstTypeChecked = true)
{
MethodInfo methodInfo = objType.GetMethod(methodName, flags);
if (methodInfo == null && objType.BaseType != null)
{
methodInfo = objType.BaseType.GetMethodInfo(methodName, flags, false);
}
if (methodInfo == null && isFirstTypeChecked)
{
throw new MissingMethodException(String.Format("Method {0}.{1} could not be found with the following BindingFlags: {2}", objType.ReflectedType.FullName, methodName, flags.ToString()));
}
return methodInfo;
}
public static FieldInfo GetFieldInfo(this Type objType, string fieldName, BindingFlags flags, bool isFirstTypeChecked = true)
{
FieldInfo fieldInfo = objType.GetField(fieldName, flags);
if (fieldInfo == null && objType.BaseType != null)
{
fieldInfo = objType.BaseType.GetFieldInfo(fieldName, flags, false);
}
if (fieldInfo == null && isFirstTypeChecked)
{
throw new MissingFieldException(String.Format("Field {0}.{1} could not be found with the following BindingFlags: {2}", objType.ReflectedType.FullName, fieldName, flags.ToString()));
}
return fieldInfo;
}
I altered bsara's implementation to be able to find private members.
I use it like this:
public static void Save(string filename, object obj)
{
try
{
using Stream s = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
var b = new BinaryFormatter();
b.Serialize(s, obj);
}
catch(SerializationException e)
{
var type= e.Message.Split("in Assembly")[0].Replace("Type", string.Empty).Replace("'", string.Empty).Trim();
var assembly=e.Message.Split("in Assembly")[1].Split("'")[1];
var atype= Type.GetType(type);
string path = FindObject(new Stack<object>(new object[] { obj }), atype, "[myself]");
throw new SerializationException($"Could not serialize path {path} in {obj.GetType().Name} due to not being able to process {type} from {assembly}. see inner exception for details", e);
}
}
the methods with a small update:
private static bool TrySerialize(object obj)
{
if(obj == null)
return true;
var stream = new MemoryStream();
var bf = new BinaryFormatter();
try
{
bf.Serialize(stream, obj);
}
catch(SerializationException)
{
return false;
}
return true;
}
private static string FindObject(Stack<object> self, Type typeToFind, string path)
{
var _self = self.Peek();
if(self.Where(x => x.Equals(_self)).Count() > 1) return null;
foreach(var prop in _self.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic).Where(x => !x.GetCustomAttributes(true).Any(y => y is XmlIgnoreAttribute)))
{
switch(prop.MemberType)
{
case System.Reflection.MemberTypes.Property:
{
var line = string.Format("{0}::{1}", path, prop.Name);
var _prop = prop as PropertyInfo;
if(_prop.GetIndexParameters().Count() > 0) break;
if(typeToFind.IsAssignableFrom(_prop.PropertyType))
return line;
if(_prop.PropertyType.IsPrimitive || _prop.PropertyType == typeof(DateTime) || _prop.PropertyType == typeof(string))
continue;
var subInst = _prop.GetValue(_self, new object[0]);
if(subInst == null)
continue;
if(!TrySerialize(subInst))
{
System.Diagnostics.Debugger.Log(0, "", string.Format("Cannot serialize {0}\n", line));
}
self.Push(subInst);
var result = FindObject(self, typeToFind, line);
self.Pop();
if(result != null)
return result;
}
break;
case System.Reflection.MemberTypes.Field:
{
var line = string.Format("{0}::*{1}", path, prop.Name);
var _prop = prop as FieldInfo;
if(typeToFind.IsAssignableFrom(_prop.FieldType))
return line;
if(_prop.FieldType.IsPrimitive || _prop.FieldType == typeof(DateTime) || _prop.FieldType == typeof(string))
continue;
var subInst = _prop.GetValue(_self);
if(subInst == null)
continue;
if(!TrySerialize(subInst))
{
System.Diagnostics.Debugger.Log(0, "", string.Format("Cannot serialize field {0}\n", line));
}
self.Push(subInst);
var result = FindObject(self, typeToFind, line);
self.Pop();
if(result != null)
return result;
}
break;
case System.Reflection.MemberTypes.Event:
{
var line = string.Format("{0}::!{1}", path, prop.Name);
var _prop = prop as EventInfo;
if(typeToFind.IsAssignableFrom(_prop.EventHandlerType))
return line;
var field = _self.GetType().GetField(_prop.Name,
BindingFlags.NonPublic |BindingFlags.Instance |BindingFlags.GetField);
if(field != null && !field.GetCustomAttributes(true).Any(x => x is NonSerializedAttribute) && !TrySerialize(field.GetValue(_self)))
{
System.Diagnostics.Debugger.Log(0, "", string.Format("Cannot serialize event {0}\n", line));
}
}
break;
case System.Reflection.MemberTypes.Custom:
{
}
break;
default: break;
}
}
if(_self is IEnumerable)
{
var list = (_self as IEnumerable).Cast<object>();
var index = 0;
foreach(var item in list)
{
index++;
self.Push(item);
var result = FindObject(self, typeToFind, string.Format("{0}[{1}]", path, index));
self.Pop();
if(result != null)
return result;
}
}
return null;
}