Related
I'm trying to use LINQ expression trees to initialize a new anonymous object with a subset of fields from a passed-in object. The subset of fields will typically be a single property but could be more (4 or so).
An example:
void DoWork(Type t)
{
//do some work to figure out which properties I want to use
List<PropertyInfo>() props = (from p in t.GetProperties()
where p.HasAttribute(MyAttribute)
select p).ToList();
//now I want to create a lambda expression that's like:
// (x=> new {x.Prop1, x.Prop2, ...})
//for each property in the list props
//create parameter
var param = Expression.Parameter(t,"t");
//get list of expressions for properties
var initValues = (from p in props
select Expression.PropertyOrField(param, p.Name)
).ToList();
//build expression body
Expression body = Expression. //<<<< I have no idea what to do here
//create lambda
dynamic propLambda = Expression.Lambda(body, param);
//use it! MyFunction( x=> new {x.Prop1, x.Prop2,...});
MyFunction(lambda);
}
The bad news: Since you're gonna build the anonymous type dynamically at runtime, it doesn't exist at compile time, as it would if the compiler encountered a new {...} in your code. There's no way around Reflection.Emit, which isn't tivial.
The good news: I've already done it, because I had the same needs. Used ILDASM to see what the compiler generates and mimic that as closely as possible.
So without further ado:
/// <summary>
/// Creates types that are much like anonymous types.
/// </summary>
public static class TupleFactory
{
// the dynamic module used to emit new types
private static readonly ModuleBuilder _module = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName { Name = nameof(TupleFactory) }, AssemblyBuilderAccess.Run).DefineDynamicModule(nameof(TupleFactory), false);
// the generic type definitions constructed so far
private static readonly Dictionary<ICollection<string>, Type> _genericTypeDefinitions = new Dictionary<ICollection<string>, Type>(CollectionComparer<string>.Default);
// the new expression factory singletons constructed so far
private static readonly Dictionary<ICollection<KeyValuePair<string, Type>>, ITupleFactory> _newExpressionFactories = new Dictionary<ICollection<KeyValuePair<string, Type>>, ITupleFactory>(new CollectionComparer<KeyValuePair<string, Type>>(KeyValueComparer<string, Type>.Default));
// some reflection objects used
private static readonly ConstructorInfo _objectCtor = typeof(object).GetConstructor(Type.EmptyTypes);
private static readonly MethodInfo _objectEquals = typeof(object).GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(object) }, null);
private static readonly MethodInfo _objectGetHashCode = typeof(object).GetMethod("GetHashCode", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null);
private static readonly MethodInfo _objectToString = typeof(object).GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null);
private static readonly MethodInfo _stringFormat = typeof(string).GetMethod("Format", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string), typeof(object[]) }, null);
private static readonly MethodInfo _equalityComparerDefaultGetter;
private static readonly MethodInfo _equalityComparerEquals;
private static readonly MethodInfo _equalityComparerGetHashCode;
static TupleFactory()
{
// init more reflection objects
_equalityComparerDefaultGetter = typeof(EqualityComparer<>).GetProperty("Default", BindingFlags.Public | BindingFlags.Static).GetGetMethod();
var eqT = typeof(EqualityComparer<>).GetGenericArguments()[0];
_equalityComparerEquals = typeof(EqualityComparer<>).GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance, null, new[] { eqT, eqT }, null);
_equalityComparerGetHashCode = typeof(EqualityComparer<>).GetMethod("GetHashCode", BindingFlags.Public | BindingFlags.Instance, null, new[] { eqT }, null);
}
/// <summary>
/// Gets a <see cref="ITupleFactory"/> singleton for a sequence of properties.
/// </summary>
/// <param name="properties">Name/Type pairs for the properties.</param>
public static ITupleFactory Create(IEnumerable<KeyValuePair<string, Type>> properties)
{
// check input
if (properties == null) throw new ArgumentNullException(nameof(properties));
var propList = properties.ToList();
if (propList.Select(p => p.Key).Distinct().Count() != propList.Count)
throw new ArgumentException("Property names must be distinct.");
lock (_module) // locks access to the static dictionaries
{
ITupleFactory result;
if (_newExpressionFactories.TryGetValue(propList, out result)) // we already have it
return result;
var propertyNames = propList.Select(p => p.Key).ToList();
Type genericTypeDefinition;
if (!_genericTypeDefinitions.TryGetValue(propertyNames, out genericTypeDefinition))
{
#region create new generic type definition
{
var typeBuilder = _module.DefineType($"<>f__AnonymousType{_newExpressionFactories.Count}`{propertyNames.Count}", TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit);
var genParams = propertyNames.Count > 0
? typeBuilder.DefineGenericParameters(propertyNames.Select(p => $"<{p}>j__TPar").ToArray())
: new GenericTypeParameterBuilder[0];
// attributes on type
var debuggerDisplay = "\\{ " + string.Join(", ", propertyNames.Select(n => $"{n} = {{{n}}}")) + " }";
// ReSharper disable AssignNullToNotNullAttribute
typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(DebuggerDisplayAttribute).GetConstructor(new[] { typeof(string) }), new object[] { debuggerDisplay }));
typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(Type.EmptyTypes), new object[0]));
// ReSharper restore AssignNullToNotNullAttribute
var fields = new List<FieldBuilder>();
var props = new List<PropertyBuilder>();
foreach (var name in propertyNames)
{
var genParam = genParams[fields.Count];
var field = typeBuilder.DefineField($"<{name}>i__Field", genParam, FieldAttributes.Private | FieldAttributes.InitOnly);
fields.Add(field);
var property = typeBuilder.DefineProperty(name, PropertyAttributes.None, genParam, null);
props.Add(property);
var getter = typeBuilder.DefineMethod($"get_{name}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, genParam, Type.EmptyTypes);
var il = getter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, field);
il.Emit(OpCodes.Ret);
property.SetGetMethod(getter);
}
#region ctor
{
// ReSharper disable once CoVariantArrayConversion
var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, genParams);
var il = ctorBuilder.GetILGenerator();
// call base class ctor
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, _objectCtor);
// assign args to fields
for (var i = 0; i < fields.Count; i++)
{
il.Emit(OpCodes.Ldarg_0);
EmitLdarg(il, i + 1);
il.Emit(OpCodes.Stfld, fields[i]);
}
il.Emit(OpCodes.Ret);
}
#endregion
#region override Equals
{
var equals = typeBuilder.DefineMethod("Equals", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(bool), new[] { typeof(object) });
typeBuilder.DefineMethodOverride(equals, _objectEquals);
var il = equals.GetILGenerator();
il.DeclareLocal(typeBuilder);
var retFalse = il.DefineLabel();
var ret = il.DefineLabel();
// local = argument as (the type being constructed)?
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Isinst, typeBuilder);
il.Emit(OpCodes.Stloc_0);
// push result of the "as" operator
il.Emit(OpCodes.Ldloc_0);
foreach (var field in fields)
{
var comparer = typeof(EqualityComparer<>).MakeGenericType(field.FieldType);
var defaultGetter = TypeBuilder.GetMethod(comparer, _equalityComparerDefaultGetter);
var equalsMethod = TypeBuilder.GetMethod(comparer, _equalityComparerEquals);
// check if the result of the previous check is false
il.Emit(OpCodes.Brfalse, retFalse);
// push EqualityComparer<FieldType>.Default.Equals(this.field, other.field)
il.Emit(OpCodes.Call, defaultGetter);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, field);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldfld, field);
il.Emit(OpCodes.Callvirt, equalsMethod);
}
// jump to the end with what was the last result
il.Emit(OpCodes.Br_S, ret);
// push false
il.MarkLabel(retFalse);
il.Emit(OpCodes.Ldc_I4_0);
il.MarkLabel(ret);
il.Emit(OpCodes.Ret);
}
#endregion
#region override GetHashCode
{
var getHashCode = typeBuilder.DefineMethod("GetHashCode", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(int), Type.EmptyTypes);
typeBuilder.DefineMethodOverride(getHashCode, _objectGetHashCode);
var il = getHashCode.GetILGenerator();
// init result with seed
il.Emit(OpCodes.Ldc_I4, HashCode.Seed);
foreach (var field in fields)
{
var comparer = typeof(EqualityComparer<>).MakeGenericType(field.FieldType);
var defaultGetter = TypeBuilder.GetMethod(comparer, _equalityComparerDefaultGetter);
var getHashCodeMethod = TypeBuilder.GetMethod(comparer, _equalityComparerGetHashCode);
// hash so far * factor
il.Emit(OpCodes.Ldc_I4, HashCode.Factor);
il.Emit(OpCodes.Mul);
// ... + EqualityComparer<FieldType>.GetHashCode(field)
il.Emit(OpCodes.Call, defaultGetter);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, field);
il.Emit(OpCodes.Callvirt, getHashCodeMethod);
il.Emit(OpCodes.Add);
}
il.Emit(OpCodes.Ret);
}
#endregion
#region override ToString
{
var toString = typeBuilder.DefineMethod("ToString", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(string), Type.EmptyTypes);
typeBuilder.DefineMethodOverride(toString, _objectToString);
var template = "{{ " + string.Join(", ", propertyNames.Select((n, i) => $"{n} = {{{i}}}")) + " }}";
var il = toString.GetILGenerator();
// push template
il.Emit(OpCodes.Ldstr, template);
// push new array
EmitLdc(il, fields.Count);
il.Emit(OpCodes.Newarr, typeof(object));
var index = 0;
foreach (var field in fields)
{
il.Emit(OpCodes.Dup); // duplicate array ref
EmitLdc(il, index); // push array index
// store boxed field in array
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, field);
il.Emit(OpCodes.Box, field.FieldType);
il.Emit(OpCodes.Stelem, typeof(object));
index++;
}
il.Emit(OpCodes.Call, _stringFormat);
il.Emit(OpCodes.Ret);
}
#endregion
genericTypeDefinition = typeBuilder.CreateType();
}
#endregion
_genericTypeDefinitions.Add(propertyNames, genericTypeDefinition);
}
var type = propList.Count == 0 ? genericTypeDefinition : genericTypeDefinition.MakeGenericType(propList.Select(p => p.Value).ToArray());
result = new TupleFactoryImpl(type, propertyNames);
_newExpressionFactories.Add(propList, result);
return result;
}
}
/// <summary>
/// Gets a <see cref="NewExpression"/> for a tuple type with the specified properties.
/// </summary>
public static NewExpression MakeNewExpression(IEnumerable<KeyValuePair<string, Expression>> properties)
{
var props = properties.ToList();
var tupleFactory = Create(props.Select(p => new KeyValuePair<string, Type>(p.Key, p.Value.Type)));
return tupleFactory.MakeNewExpression(props.Select(p => p.Value));
}
private sealed class TupleFactoryImpl : ITupleFactory
{
public Type TupleType { get; }
private readonly ConstructorInfo _ctor;
private readonly MemberInfo[] _properties;
public TupleFactoryImpl(Type tupleType, IEnumerable<string> propertyNames)
{
TupleType = tupleType;
_ctor = tupleType.GetConstructors().Single();
var propsByName = tupleType.GetProperties().ToDictionary(p => p.Name);
_properties = propertyNames.Select(name => (MemberInfo)propsByName[name]).ToArray();
}
public NewExpression MakeNewExpression(IEnumerable<Expression> arguments)
{
return Expression.New(_ctor, arguments, _properties);
}
}
/// <summary>
/// Helper function to pick the optimal op code.
/// </summary>
private static void EmitLdarg(ILGenerator il, int index)
{
if (index < 0) throw new ArgumentOutOfRangeException();
switch (index)
{
case 0: il.Emit(OpCodes.Ldarg_0); break;
case 1: il.Emit(OpCodes.Ldarg_1); break;
case 2: il.Emit(OpCodes.Ldarg_2); break;
case 3: il.Emit(OpCodes.Ldarg_3); break;
default:
if (index <= byte.MaxValue)
il.Emit(OpCodes.Ldarg_S, (byte)index);
else if (index <= short.MaxValue)
il.Emit(OpCodes.Ldarg, (short)index);
else
throw new ArgumentOutOfRangeException();
break;
}
}
/// <summary>
/// Helper function to pick the optimal op code.
/// </summary>
private static void EmitLdc(ILGenerator il, int i)
{
switch (i)
{
case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
case 0: il.Emit(OpCodes.Ldc_I4_0); break;
case 1: il.Emit(OpCodes.Ldc_I4_1); break;
case 2: il.Emit(OpCodes.Ldc_I4_2); break;
case 3: il.Emit(OpCodes.Ldc_I4_3); break;
case 4: il.Emit(OpCodes.Ldc_I4_4); break;
case 5: il.Emit(OpCodes.Ldc_I4_5); break;
case 6: il.Emit(OpCodes.Ldc_I4_6); break;
case 7: il.Emit(OpCodes.Ldc_I4_7); break;
case 8: il.Emit(OpCodes.Ldc_I4_8); break;
default:
if (i >= byte.MinValue && i <= byte.MaxValue)
il.Emit(OpCodes.Ldc_I4_S, (byte)i);
else
il.Emit(OpCodes.Ldc_I4, i);
break;
}
}
}
/// <summary>
/// Compute a hash code.
/// </summary>
public struct HashCode
{
// magic numbers for hash code
public const int Seed = 0x20e699b;
public const int Factor = unchecked((int)0xa5555529);
private readonly int? _value;
private HashCode(int value)
{
_value = value;
}
/// <summary>
/// Convert to the actual hash code based on what was added so far.
/// </summary>
public static implicit operator int(HashCode hc) => hc._value ?? 0;
/// <summary>
/// Add a hash code to the state.
/// </summary>
/// <returns>An updated <see cref="HashCode"/>.</returns>
public static HashCode operator +(HashCode hc, int other) => new HashCode(unchecked((hc._value == null ? Seed : hc._value.Value * Factor) + other));
/// <summary>
/// Add a sequence of hash code to the state.
/// </summary>
/// <returns>An updated <see cref="HashCode"/>.</returns>
public static HashCode operator +(HashCode hc, IEnumerable<int> others) => others.Aggregate(hc, (a, c) => a + c);
}
/// <summary>
/// <see cref="IEqualityComparer{T}"/> for <see cref="KeyValuePair{TKey, TValue}"/>.
/// </summary>
public sealed class KeyValueComparer<TKey, TValue> : IEqualityComparer<KeyValuePair<TKey, TValue>>
{
/// <summary>
/// Gets the singleton.
/// </summary>
public static KeyValueComparer<TKey, TValue> Default { get; } = new KeyValueComparer<TKey, TValue>();
private readonly IEqualityComparer<TKey> _keyComparer;
private readonly IEqualityComparer<TValue> _valueComparer;
/// <summary>
/// Initialize by specifying <see cref="IEqualityComparer{T}"/>s for key and value.
/// </summary>
public KeyValueComparer(IEqualityComparer<TKey> keyComparer = null, IEqualityComparer<TValue> valueComparer = null)
{
_keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;
_valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
}
/// <summary>
/// Equality.
/// </summary>
public bool Equals(KeyValuePair<TKey, TValue> x, KeyValuePair<TKey, TValue> y) => _keyComparer.Equals(x.Key, y.Key) && _valueComparer.Equals(x.Value, y.Value);
/// <summary>
/// Hash code.
/// </summary>
public int GetHashCode(KeyValuePair<TKey, TValue> obj) => new HashCode() + _keyComparer.GetHashCode(obj.Key) + _valueComparer.GetHashCode(obj.Value);
}
/// <summary>
/// <see cref="IEqualityComparer{T}"/> for a collection.
/// </summary>
public sealed class CollectionComparer<TElement> : IEqualityComparer<ICollection<TElement>>
{
/// <summary>
/// Gets an instance using <see cref="EqualityComparer{T}.Default"/> as the element comparer.
/// </summary>
public static CollectionComparer<TElement> Default { get; } = new CollectionComparer<TElement>();
private readonly IEqualityComparer<TElement> _elementComparer;
/// <summary>
/// Initialize with a specific element comparer.
/// </summary>
public CollectionComparer(IEqualityComparer<TElement> elementComparer = null)
{
_elementComparer = elementComparer ?? EqualityComparer<TElement>.Default;
}
/// <summary>
/// Determines whether the specified objects are equal.
/// </summary>
/// <returns>
/// true if the specified objects are equal; otherwise, false.
/// </returns>
public bool Equals(ICollection<TElement> x, ICollection<TElement> y)
{
if (x == null) return y == null;
if (y == null) return false;
return x.Count == y.Count && x.SequenceEqual(y, _elementComparer);
}
/// <summary>
/// Returns a hash code for the specified object.
/// </summary>
/// <returns>
/// A hash code for the specified object.
/// </returns>
/// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param>
public int GetHashCode(ICollection<TElement> obj)
{
var result = new HashCode() + typeof(TElement).GetHashCode();
if (obj == null) return result;
result += obj.Count;
result += obj.Select(element => _elementComparer.GetHashCode(element));
return result;
}
}
Usage:
void DoWork(Type type)
{
var props = type.GetProperties().Where(p => p.HasAttribute(MyAttribute)).ToList();
var tupleFactory = TupleFactory.Create(props.Select(p => new KeyValuePair<string, Type>(p.Name, p.PropertyType)));
var param = Expression.Parameter(type, "x");
var newEx = tupleFactory.MakeNewExpression(props.Select(p => Expression.Property(param, p)));
var lambda = Expression.Lambda(newEx, param); // <-- type is LambdaExpression, not dynamic
MyFunction(lambda);
}
I'm trying to create a class instance from my Linq query result. Since I have to do this for many classes, I'm trying to find the most suitable shortcut. I'm wondering whether I can make the "select" part of the query any shorter.
My class:
public class current_control_id_class
{
public string asset_class { get; set; }
public string region_code { get; set; }
public string instance_code { get; set; }
public int sdi_control_id { get; set; }
public int rows_loaded { get; set; }
}
My assignment function:
foreach (var results in query)
{
foreach (PropertyInfo result in results.GetType().GetProperties())
{
string name = result.Name;
foreach (PropertyInfo info in used.GetType().GetProperties())
{
if (result.Name == info.Name)
{
Console.WriteLine("Result {0} matches class {1} and the value is {2}", result.Name, info.Name, result.GetValue(results,null));
}
}
}
}
My query (i know this works)
current_control_id_class used = new current_control_id_class();
var query =
from result in t_sdi_current_control_id.AsQueryable()
where result.asset_class == asset_class
&& result.region_code == region
&& result.instance_code == site
select new current_control_id_class() {
rows_loaded = result.rows_loaded,
sdi_control_id = result.sdi_control_id,
asset_class = result.asset_class,
hsbc_region_code = result.hsbc_region_code,
hsbc_instance_code = result.hsbc_instance_code
};
You might be able to use AutoMapper to map instances of t_sdi_current_control_id to instances of current_control_id_class:
First initialise the mapping:
Mapper.CreateMap<t_sdi_current_control_id, current_control_id_class>();
Then use it:
var query =
from result in t_sdi_current_control_id.AsQueryable()
where result.asset_class == asset_class
&& result.region_code == region
&& result.instance_code == site
select Mapper.Map<current_control_id_class>(result);
If you don't want to use a third-party library, here's some tested code to do it for you:
/// <summary>
/// Maps instances of <typeparam name="TSource"/> to new instances of
/// <typeparam name="TDestination"/> by copying across accessible public
/// instance properties whose names match.
/// </summary>
/// <remarks>
/// Internally uses a compiled Expression, so mapping should be quick at
/// the expense of <see cref="Mapper"/> initialisation.
/// </remarks>
public class Mapper<TSource, TDestination>
where TDestination : new()
{
readonly Func<TSource, TDestination> map;
public Mapper()
{
this.map = GenerateMapping();
}
static Func<TSource, TDestination> GenerateMapping()
{
var sourceProperties = GetPublicInstancePropertiesWithAccessors<TSource>(property => property.GetGetMethod());
var destinationProperties = GetPublicInstancePropertiesWithAccessors<TDestination>(property => property.GetSetMethod());
var source = Expression.Parameter(typeof(TSource));
var destination = Expression.Variable(typeof(TDestination));
var copyPropertyValues = from sourceProperty in sourceProperties
from destinationProperty in destinationProperties
where sourceProperty.Name.Equals(destinationProperty.Name, StringComparison.Ordinal)
select Expression.Assign(
Expression.Property(destination, destinationProperty),
Expression.Property(source, sourceProperty)
);
var variables = new[] { destination };
var assignNewDestinationInstance = Expression.Assign(destination, Expression.New(typeof(TDestination)));
var returnDestinationInstance = new Expression[] { destination };
var statements =
new[] { assignNewDestinationInstance }
.Concat(copyPropertyValues)
.Concat(returnDestinationInstance);
var body = Expression.Block(variables, statements);
var parameters = new[] { source };
var method = Expression.Lambda<Func<TSource, TDestination>>(body, parameters);
return method.Compile();
}
/// <summary>
/// Gets public instance properties of <typeparamref name="T"/> that
/// have accessible accessors defined by <paramref name="getAccessor"/>.
/// </summary>
static IEnumerable<PropertyInfo> GetPublicInstancePropertiesWithAccessors<T>(Func<PropertyInfo, MethodInfo> getAccessor)
{
var type = typeof(T);
var publicInstanceProperties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
return from property in publicInstanceProperties
let accessor = getAccessor(property)
where accessor != null
select property;
}
public TDestination Map(TSource source)
{
return map(source);
}
}
Use it like this:
//Keep this around so it gets re-used.
var mapper = new Mapper<t_sdi_current_control_id, current_control_id_class>();
var result = mapper.Map(value);
Using TypeGenerator Class
public class TypeGenerator
{
/// <summary>
/// internal cache for already generated types
/// </summary>
private static Dictionary<Type, Type> asyncTypeCache = new Dictionary<Type, Type>();
/// <summary>
/// provides a cache for the modules
/// </summary>
private static Dictionary<string, ModuleBuilder> moduleBuilderCache = new Dictionary<string, ModuleBuilder>();
/// <summary>
/// Generates the Async version of the TSync type.
/// the generate type repects the AsyncPattern and it is already decorated with attributes for WCF operations
/// </summary>
/// <typeparam name="TSync">The Sync version of type</typeparam>
/// <returns>A type that is the Async version of the TSync type, that implements the AsyncPattern for WCF</returns>
public Type GenerateAsyncInterfaceFor<TSync>() where TSync : class
{
Type syncType = typeof(TSync);
if (asyncTypeCache.ContainsKey(syncType)) return asyncTypeCache[syncType];
if (!syncType.IsInterface) throw new InvalidOperationException("Only interface type could be transformed");
var asynchAssemblyName = string.Format("{0}.Async", syncType.Namespace);
TypeBuilder typeBuilder =
GetModuleBuilder(asynchAssemblyName)
.DefineType(
string.Format("{0}.Async.{1}", syncType.Namespace, syncType.Name),
(syncType.IsPublic ? TypeAttributes.Public : 0) |
TypeAttributes.Abstract |
TypeAttributes.Interface);
foreach (var method in syncType.GetAllInterfaceMethods())
{
AddBeginAsynchVersionForMethod(typeBuilder, method, #"http://tempuri.org");
AddEndAsynchVersionForMethod(typeBuilder, method);
}
var serviceContractConstructor = typeof(ServiceContractAttribute).GetConstructor(new Type[0]);
var attribuiteBuilder =
new CustomAttributeBuilder(
serviceContractConstructor,
new object[0]);
typeBuilder.SetCustomAttribute(attribuiteBuilder);
Type asyncType = typeBuilder.CreateType();
asyncTypeCache.Add(syncType, asyncType);
return asyncType;
}
/// <summary>
/// Creates a End verison of a sync method, that implements the AsyncPattern
/// </summary>
/// <param name="typeBuilder">the tipebuilder where the type is being building</param>
/// <param name="method">information about the sync version of the method</param>
private void AddEndAsynchVersionForMethod(TypeBuilder typeBuilder, MethodInfo method)
{
string endMethodName = string.Format("End{0}", method.Name);
var parameters =
method.GetParameters()
.Select(x =>
new
{
Type = x.ParameterType,
Name = x.Name,
Attributes = x.Attributes,
})
.ToList();
parameters.Add(
new
{
Type = typeof(IAsyncResult),
Name = "asyncResult",
Attributes = ParameterAttributes.None,
});
var methodBuilder =
typeBuilder
.DefineMethod(
endMethodName,
method.Attributes,
method.CallingConvention,
method.ReturnType,
parameters.Select(x => x.Type).ToArray());
for (int i = 0; i < parameters.Count(); i++)
{
var parameter = parameters[i];
methodBuilder.DefineParameter(i + 1, parameter.Attributes, parameter.Name);
}
}
/// <summary>
/// Creates a Begin verison of a sync method, that implements the AsyncPattern
/// </summary>
/// <param name="typeBuilder">the tipebuilder where the type is being building</param>
/// <param name="method">information about the sync version of the method</param>
private void AddBeginAsynchVersionForMethod(TypeBuilder typeBuilder, MethodInfo method, string nameSpace)
{
string beginMethodName = string.Format("Begin{0}", method.Name);
var parametersTypeList = method.GetParameters().Select(x => x.ParameterType).ToList();
var parametersNameList = method.GetParameters().Select(x => x.Name).ToList();
var parametersAttributeList = method.GetParameters().Select(x => x.Attributes).ToList();
parametersTypeList.Add(typeof(AsyncCallback));
parametersAttributeList.Add(ParameterAttributes.None);
parametersNameList.Add("callBack");
parametersTypeList.Add(typeof(object));
parametersAttributeList.Add(ParameterAttributes.None);
parametersNameList.Add("statusObject");
var methodBuilder =
typeBuilder
.DefineMethod(
beginMethodName,
method.Attributes,
method.CallingConvention,
typeof(IAsyncResult),
parametersTypeList.ToArray());
for (int i = 0; i < parametersTypeList.Count(); i++)
{
methodBuilder.DefineParameter(i + 1, parametersAttributeList[i], parametersNameList[i]);
}
var operationContractConstructor = typeof(OperationContractAttribute).GetConstructor(new Type[0]);
var asynchPatternProperty = typeof(OperationContractAttribute).GetProperty("AsyncPattern");
var actionProperty = typeof(OperationContractAttribute).GetProperty("Action");
var actionValue = string.Format("{0}/{1}/{2}", nameSpace, method.DeclaringType.Name, method.Name);
var replyActionProperty = typeof(OperationContractAttribute).GetProperty("ReplyAction");
var replyActionValue = string.Format("{0}/{1}/{2}Response", nameSpace, method.DeclaringType.Name, method.Name);
var attribuiteBuilder =
new CustomAttributeBuilder(
operationContractConstructor,
new object[0],
new[] { asynchPatternProperty, actionProperty, replyActionProperty },
new object[] { true, actionValue, replyActionValue });
methodBuilder.SetCustomAttribute(attribuiteBuilder);
}
/// <summary>
/// provides a ModelBuilder with the required assembly name
/// </summary>
/// <param name="requiredAssemblyName">the assembly name for where the type will be generated in</param>
/// <returns>a model builder</returns>
/// <remarks>in this version the model builder is not cached, it could be interesting to generate all the types in the same assembly by caching the model builder</remarks>
private ModuleBuilder GetModuleBuilder(string requiredAssemblyName)
{
if (moduleBuilderCache.ContainsKey(requiredAssemblyName))
{
return moduleBuilderCache[requiredAssemblyName];
}
AssemblyName assemblyName = new AssemblyName(requiredAssemblyName);
AssemblyBuilder assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName,
AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
moduleBuilderCache[requiredAssemblyName] = moduleBuilder;
return moduleBuilder;
}
}
and TypeExtentions Class
public static class TypeExtensions
{
/// <summary>
/// extracts all the methods fromthe given interface type and from all the inherited ones too
/// </summary>
/// <param name="type">the type fromwhich extracts all the methods</param>
/// <returns>list of MemberInfo representing the methods</returns>
public static IEnumerable<MethodInfo> GetAllInterfaceMethods(this Type type)
{
IEnumerable<MethodInfo> methods = type.GetMethods();
foreach (var subType in type.GetInterfaces())
{
methods = methods.Union(GetAllInterfaceMethods(subType));
}
return methods;
}
}
I 'm able to convert a Synchronous Interface to Asynchronous one.
How to use DuplexChannelFactory with a pre-defined reflection Asynchronous interface ?
var myInterface = new TypeGenerator ( ) . GenerateAsyncInterfaceFor<mySynchronousInterface> ( );
var Client = new DuplexChannelFactory<myInterface> ( new InstanceContext ( new myClass ( ) ) , "myConfiguration" );
I'm getting the following issue:
The type or namespace name 'myInterface' could not be found (are you
missing a using directive or an assembly reference?)
I'm trying to code a mid-tier WCF interface.
The syntax you are using is for compile time typing, of course the types you are making are generated at runtime, so it isn't going to work. It is possible though.
How to: Examine and Instantiate Generic Types with Reflection
Here's how the code will look (I'm assuming myClass was already defined with source code, if not it would just be another Activator.CreateInstance call)
Type t = typeof(DuplexChannelFactory<>);
Type typedDuplexChannelFactory = t.MakeGenericType(new Type[]{myInterface});
var Client = Activator.CreateInstance(typedDuplexChannelFactory, new object[]{new InstanceContext ( new myClass ( ) ) , "myConfiguration"});
The name properties of System.Type class return a strange result in case of generic types. Is there a way to get the type name in a format closer to the way I specified it?
Example: typeof(List<string>).OriginalName == "List<string>"
The problem with "pretty" names is they are different depending on the language you are using. Imagine the surprise of a VB.NET developer if OriginalName returned C# syntax.
However, it's pretty fairly easy to make this yourself:
private static string PrettyName(Type type)
{
if (type.GetGenericArguments().Length == 0)
{
return type.Name;
}
var genericArguments = type.GetGenericArguments();
var typeDefinition = type.Name;
var unmangledName = typeDefinition.Substring(0, typeDefinition.IndexOf("`"));
return unmangledName + "<" + String.Join(",", genericArguments.Select(PrettyName)) + ">";
}
This will recursively resolve the unmanaged name, so that if you have something like Dictionary<string, IList<string>> it should still work.
I used CodeDomProvider to convert to c#:
public static string GetOriginalName(this Type type)
{
string TypeName = type.FullName.Replace(type.Namespace + ".", "");//Removing the namespace
var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"); //You can also use "VisualBasic"
var reference = new System.CodeDom.CodeTypeReference(TypeName);
return provider.GetTypeOutput(reference);
}
Like Harold Hoyer's answer but including nullables and a few more built-in types:
/// <summary>
/// Get full type name with full namespace names
/// </summary>
/// <param name="type">
/// The type to get the C# name for (may be a generic type or a nullable type).
/// </param>
/// <returns>
/// Full type name, fully qualified namespaces
/// </returns>
public static string CSharpName(this Type type)
{
Type nullableType = Nullable.GetUnderlyingType(type);
string nullableText;
if (nullableType != null)
{
type = nullableType;
nullableText = "?";
}
else
{
nullableText = string.Empty;
}
if (type.IsGenericType)
{
return string.Format(
"{0}<{1}>{2}",
type.Name.Substring(0, type.Name.IndexOf('`')),
string.Join(", ", type.GetGenericArguments().Select(ga => ga.CSharpName())),
nullableText);
}
switch (type.Name)
{
case "String":
return "string";
case "Int32":
return "int" + nullableText;
case "Decimal":
return "decimal" + nullableText;
case "Object":
return "object" + nullableText;
case "Void":
return "void" + nullableText;
default:
return (string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName) + nullableText;
}
}
Here is my implementation. It was created to describe methods, so it handles the ref and out keywords.
private static Dictionary<Type, string> shorthandMap = new Dictionary<Type, string>
{
{ typeof(Boolean), "bool" },
{ typeof(Byte), "byte" },
{ typeof(Char), "char" },
{ typeof(Decimal), "decimal" },
{ typeof(Double), "double" },
{ typeof(Single), "float" },
{ typeof(Int32), "int" },
{ typeof(Int64), "long" },
{ typeof(SByte), "sbyte" },
{ typeof(Int16), "short" },
{ typeof(String), "string" },
{ typeof(UInt32), "uint" },
{ typeof(UInt64), "ulong" },
{ typeof(UInt16), "ushort" },
};
private static string CSharpTypeName(Type type, bool isOut = false)
{
if (type.IsByRef)
{
return String.Format("{0} {1}", isOut ? "out" : "ref", CSharpTypeName(type.GetElementType()));
}
if (type.IsGenericType)
{
if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return String.Format("{0}?", CSharpTypeName(Nullable.GetUnderlyingType(type)));
}
else
{
return String.Format("{0}<{1}>", type.Name.Split('`')[0],
String.Join(", ", type.GenericTypeArguments.Select(a => CSharpTypeName(a)).ToArray()));
}
}
if (type.IsArray)
{
return String.Format("{0}[]", CSharpTypeName(type.GetElementType()));
}
return shorthandMap.ContainsKey(type) ? shorthandMap[type] : type.Name;
}
The calling code looks like this:
string line = String.Format("{0}.{1}({2})",
method.DeclaringType.Name,
method.Name,
String.Join(", ", method.GetParameters().Select(p => CSharpTypeName(p.ParameterType, p.IsOut) + " " + p.Name).ToArray()));
Where method is a MethodInfo instance.
One note: I didn't have a need to describe any multi-dimensional array types, so I didn't bother implementing description for that, but it would be fairly easy to add by calling type.GetArrayRank().
You have to write this yourself. Keep in mind that Type.Name etc. are invoking methods that live in the CLR and can be invoked from multiple languages. This is why they don't come back looking like C# or VB or the language the caller was coded in, but instead looking like the CLR representation.
Note further that string and what not are aliases for CLR types like System.String. Again, this plays a role in the formatting that you see.
It's not hard to do using reflection, but I question the value of it.
A minimal work solution that leverages CodeDomProvider is to control how the CodeTypeReference instance is built in the first place. There are special cases only for generic types and multi-rank arrays, so we only have to care about those:
static CodeTypeReference CreateTypeReference(Type type)
{
var typeName = (type.IsPrimitive || type == typeof(string)) ? type.FullName : type.Name;
var reference = new CodeTypeReference(typeName);
if (type.IsArray)
{
reference.ArrayElementType = CreateTypeReference(type.GetElementType());
reference.ArrayRank = type.GetArrayRank();
}
if (type.IsGenericType)
{
foreach (var argument in type.GetGenericArguments())
{
reference.TypeArguments.Add(CreateTypeReference(argument));
}
}
return reference;
}
Using this modified factory method, it is then possible to use the appropriate code provider to get pretty typing, like so:
using (var provider = new CSharpCodeProvider())
{
var reference = CreateTypeReference(typeof(IObservable<IEnumerable<Tuple<int?, string>>>[,]));
var output = provider.GetTypeOutput(reference);
Console.WriteLine(output);
}
The above code returns IObservable<IEnumerable<Tuple<Nullable<int>, string>>>[,]. The only special case that is not handled well are Nullable types but this is really more a fault of the CodeDomProvider than anything else.
First: Kudos to Navid for wheel reinvention avoidance. I would upvote if I could.
Here are a few points to add, if anyone goes down this path (at least for VS10/.Net 4):
* Try using one of the variants of CodeTypeReference with Type arguments. This avoids a number of pitfalls of using string type names (such as trailing &) and means you get back bool instead of System.Boolean etc. You do get back the full namespace for a lot of types like this but you can always get rid of the namespace part later.
* Simple Nullables tend to come back in the form System.Nullable<int> rather than int? - You can convert to the nicer syntax with a regex on the answer such as:
const string NullablePattern = #"System.Nullable<(?<nulledType>[\w\.]+)>";
const string NullableReplacement = #"${nulledType}?";
answer = Regex.Replace(answer, NullablePattern, NullableReplacement);
* The Type of a method argument like out T? gives a very opaque string. If anyone has an elegant way of dealing with things like this I would love to know about it.Hopefully this all becomes very easy with Roslyn.
as in your example you can expect the type so you can try that
public class test<T> where T : class
{
public List<String> tt
{
get;
set;
}
}
///////////////////////////
test<List<String>> tt = new test<List<String>>();
if(tt.GetType().FullName.Contains(TypeOf(List<String>).FullName))
{
//do something
}
else
{
//do something else
}
I understand that you want to compare types.
The best way to do this is...
myVar is List<string> or
myVar.GetType() == myOtherVar.GetType()
If you don't need this... please disregard my answer.
I understood, that I have to write this myself. Here is my solution (it is actually a bit more than asked for). It is, probably, helpfull.
using System.Reflection;
using HWClassLibrary.Debug;
using System.Collections.Generic;
using System.Linq;
using System;
namespace HWClassLibrary.Helper
{
public static class TypeNameExtender
{
private static IEnumerable<Type> _referencedTypesCache;
public static void OnModuleLoaded() { _referencedTypesCache = null; }
public static string PrettyName(this Type type)
{
if(type == typeof(int))
return "int";
if(type == typeof(string))
return "string";
var result = PrettyTypeName(type);
if(type.IsGenericType)
result = result + PrettyNameForGeneric(type.GetGenericArguments());
return result;
}
private static string PrettyTypeName(Type type)
{
var result = type.Name;
if(type.IsGenericType)
result = result.Remove(result.IndexOf('`'));
if (type.IsNested && !type.IsGenericParameter)
return type.DeclaringType.PrettyName() + "." + result;
if(type.Namespace == null)
return result;
var conflictingTypes = ReferencedTypes
.Where(definedType => definedType.Name == type.Name && definedType.Namespace != type.Namespace)
.ToArray();
var namespaceParts = type.Namespace.Split('.').Reverse().ToArray();
var namespacePart = "";
for(var i = 0; i < namespaceParts.Length && conflictingTypes.Length > 0; i++)
{
namespacePart = namespaceParts[i] + "." + namespacePart;
conflictingTypes = conflictingTypes
.Where(conflictingType => (conflictingType.Namespace + ".").EndsWith(namespacePart))
.ToArray();
}
return namespacePart + result;
}
private static IEnumerable<Type> ReferencedTypes
{
get
{
if(_referencedTypesCache == null)
_referencedTypesCache = Assembly.GetEntryAssembly().GetReferencedTypes();
return _referencedTypesCache;
}
}
private static string PrettyNameForGeneric(Type[] types)
{
var result = "";
var delim = "<";
foreach(var t in types)
{
result += delim;
delim = ",";
result += t.PrettyName();
}
return result + ">";
}
}
}
I do it like this ..
public static class TypeExtensions {
public static String GetName(this Type t) {
String readable;
#if PREVENT_RECURSION
if(m_names.TryGetValue(t, out readable)) {
return readable;
}
#endif
var tArgs = t.IsGenericType ? t.GetGenericArguments() : Type.EmptyTypes;
var name = t.Name;
var format = Regex.Replace(name, #"`\d+.*", "")+(t.IsGenericType ? "<?>" : "");
var names = tArgs.Select(x => x.IsGenericParameter ? "" : GetName(x));
readable=String.Join(String.Join(",", names), format.Split('?'));
#if PREVENT_RECURSION
m_names.Add(t, readable);
#endif
return readable;
}
static readonly Dictionary<Type, String> m_names = new Dictionary<Type, String> { };
}
where PREVENT_RECURSION should be defined true if you don't want the type in generic type arguments be resolved recursively every time, just take from the cache dictionary; leave it undefined of false if you don't care that.
Combined a few answers, added support for nested types and array ranks, and turned it into an extension method with cached resolved names.
/// <summary>
/// Extension methods for <see cref="Type"/>.
/// </summary>
public static class TypeExtensions
{
/// <summary>
/// Dictionary of type names.
/// </summary>
private static readonly ConcurrentDictionary<Type, string> typeNames;
/// <summary>
/// Initializes static members of the <see cref="TypeExtensions"/> class.
/// </summary>
static TypeExtensions()
{
typeNames = new ConcurrentDictionary<Type, string>
{
[typeof(bool)] = "bool",
[typeof(byte)] = "byte",
[typeof(char)] = "char",
[typeof(decimal)] = "decimal",
[typeof(double)] = "double",
[typeof(float)] = "float",
[typeof(int)] = "int",
[typeof(long)] = "long",
[typeof(sbyte)] = "sbyte",
[typeof(short)] = "short",
[typeof(string)] = "string",
[typeof(uint)] = "uint",
[typeof(ulong)] = "ulong",
[typeof(ushort)] = "ushort"
};
}
/// <summary>
/// Gets the type name with generics and array ranks resolved.
/// </summary>
/// <param name="type">
/// The type whose name to resolve.
/// </param>
/// <returns>
/// The resolved type name.
/// </returns>
public static string ToCSTypeName(this Type type)
{
return typeNames.GetOrAdd(type, GetPrettyTypeName);
}
/// <summary>
/// Gets the type name as it would be written in C#
/// </summary>
/// <param name="type">
/// The type whose name is to be written.
/// </param>
/// <returns>
/// The type name as it is written in C#
/// </returns>
private static string GetPrettyTypeName(Type type)
{
var typeNamespace = type.DeclaringType != null ? ToCSTypeName(type.DeclaringType) : type.Namespace;
if (type.IsGenericType)
{
if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return $"{ToCSTypeName(Nullable.GetUnderlyingType(type))}?";
}
var typeList = string.Join(", ", type.GenericTypeArguments.Select(ToCSTypeName).ToArray());
var typeName = type.Name.Split('`')[0];
return $"{typeNamespace}.{typeName}<{typeList}>";
}
if (type.IsArray)
{
var arrayRank = string.Empty.PadLeft(type.GetArrayRank() - 1, ',');
var elementType = ToCSTypeName(type.GetElementType());
return $"{elementType}[{arrayRank}]";
}
return $"{typeNamespace}.{type.Name}";
}
}
public static string pretty_name( Type type, int recursion_level = -1, bool expand_nullable = false )
{
if( type.IsArray )
{
return $"{pretty_name( type.GetElementType(), recursion_level, expand_nullable )}[]";
}
if( type.IsGenericType )
{
// find generic type name
var gen_type_name = type.GetGenericTypeDefinition().Name;
var index = gen_type_name.IndexOf( '`' );
if( index != -1 )
gen_type_name = gen_type_name.Substring( 0, index );
// retrieve generic type aguments
var arg_names = new List<string>();
var gen_type_args = type.GetGenericArguments();
foreach( var gen_type_arg in gen_type_args )
{
arg_names.Add(
recursion_level != 0
? pretty_name( gen_type_arg, recursion_level - 1, expand_nullable )
: "?" );
}
// if type is nullable and want compact notation '?'
if( !expand_nullable && Nullable.GetUnderlyingType( type ) != null )
return $"{arg_names[ 0 ]}?";
// compose common generic type format "T<T1, T2, ...>"
return $"{gen_type_name}<{string.Join( ", ", arg_names )}>";
}
return type.Name;
}
My two cents:
Everything is done through the Type interface
No Regex
No extra objects created except for the list that holds generic argument names
Supports infinite recursion or recursion to a certain level (or no recursion at all!)
Supports nullable types (both formats "Nullable<>" and "?")
Supports ranked arrays
My question is shown in this code
I have class like that
public class MainCS
{
public int A;
public int B;
public int C;
public int D;
}
public class Sub1
{
public int A;
public int B;
public int C;
}
public void MethodA(Sub1 model)
{
MainCS mdata = new MainCS() { A = model.A, B = model.B, C = model.C };
// is there a way to directly cast class Sub1 into MainCS like that
mdata = (MainCS) model;
}
Use JSON serialization and deserialization:
using Newtonsoft.Json;
Class1 obj1 = new Class1();
Class2 obj2 = JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj1));
Or:
public class Class1
{
public static explicit operator Class2(Class1 obj)
{
return JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj));
}
}
Which then allows you to do something like
Class1 obj1 = new Class1();
Class2 obj2 = (Class2)obj1;
You have already defined the conversion, you just need to take it one step further if you would like to be able to cast. For example:
public class sub1
{
public int a;
public int b;
public int c;
public static explicit operator maincs(sub1 obj)
{
maincs output = new maincs() { a = obj.a, b = obj.b, c = obj.c };
return output;
}
}
Which then allows you to do something like
static void Main()
{
sub1 mySub = new sub1();
maincs myMain = (maincs)mySub;
}
What he wants to say is:
"If you have two classes which share most of the same properties you can cast an object from class a to class b and automatically make the system understand the assignment via the shared property names?"
Option 1: Use reflection
Disadvantage : It's gonna slow you down more than you think.
Option 2: Make one class derive from another, the first one with common properties and other an extension of that.
Disadvantage: Coupled! if your're doing that for two layers in your application then the two layers will be coupled!
Let there be:
class customer
{
public string firstname { get; set; }
public string lastname { get; set; }
public int age { get; set; }
}
class employee
{
public string firstname { get; set; }
public int age { get; set; }
}
Now here is an extension for Object type:
public static T Cast<T>(this Object myobj)
{
Type objectType = myobj.GetType();
Type target = typeof(T);
var x = Activator.CreateInstance(target, false);
var z = from source in objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property select source ;
var d = from source in target.GetMembers().ToList()
where source.MemberType == MemberTypes.Property select source;
List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
value = myobj.GetType().GetProperty(memberInfo.Name).GetValue(myobj,null);
propertyInfo.SetValue(x,value,null);
}
return (T)x;
}
Now you use it like this:
static void Main(string[] args)
{
var cus = new customer();
cus.firstname = "John";
cus.age = 3;
employee emp = cus.Cast<employee>();
}
Method cast checks common properties between two objects and does the assignment automatically.
You could change your class structure to:
public class maincs : sub1
{
public int d;
}
public class sub1
{
public int a;
public int b;
public int c;
}
Then you could keep a list of sub1 and cast some of them to mainc.
By using following code you can copy any class object to another class object for same name and same type of properties.
public class CopyClass
{
/// <summary>
/// Copy an object to destination object, only matching fields will be copied
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sourceObject">An object with matching fields of the destination object</param>
/// <param name="destObject">Destination object, must already be created</param>
public static void CopyObject<T>(object sourceObject, ref T destObject)
{
// If either the source, or destination is null, return
if (sourceObject == null || destObject == null)
return;
// Get the type of each object
Type sourceType = sourceObject.GetType();
Type targetType = destObject.GetType();
// Loop through the source properties
foreach (PropertyInfo p in sourceType.GetProperties())
{
// Get the matching property in the destination object
PropertyInfo targetObj = targetType.GetProperty(p.Name);
// If there is none, skip
if (targetObj == null)
continue;
// Set the value in the destination
targetObj.SetValue(destObject, p.GetValue(sourceObject, null), null);
}
}
}
Call Method Like,
ClassA objA = new ClassA();
ClassB objB = new ClassB();
CopyClass.CopyObject(objOfferMast, ref objB);
It will copy objA into objB.
You can provide an explicit overload for the cast operator:
public static explicit operator maincs(sub1 val)
{
var ret = new maincs() { a = val.a, b = val.b, c = val.c };
return ret;
}
Another option would be to use an interface that has the a, b, and c properties and implement the interface on both of the classes. Then just have the parameter type to methoda be the interface instead of the class.
Using this code you can copy any class object to another class object for same name and same type of properties.
JavaScriptSerializer JsonConvert = new JavaScriptSerializer();
string serializeString = JsonConvert.Serialize(objectEntity);
objectViewModel objVM = JsonConvert.Deserialize<objectViewModel>(serializeString);
There are some great answers here, I just wanted to add a little bit of type checking here as we cannot assume that if properties exist with the same name, that they are of the same type. Here is my offering, which extends on the previous, very excellent answer as I had a few little glitches with it.
In this version I have allowed for the consumer to specify fields to be excluded, and also by default to exclude any database / model specific related properties.
public static T Transform<T>(this object myobj, string excludeFields = null)
{
// Compose a list of unwanted members
if (string.IsNullOrWhiteSpace(excludeFields))
excludeFields = string.Empty;
excludeFields = !string.IsNullOrEmpty(excludeFields) ? excludeFields + "," : excludeFields;
excludeFields += $"{nameof(DBTable.ID)},{nameof(DBTable.InstanceID)},{nameof(AuditableBase.CreatedBy)},{nameof(AuditableBase.CreatedByID)},{nameof(AuditableBase.CreatedOn)}";
var objectType = myobj.GetType();
var targetType = typeof(T);
var targetInstance = Activator.CreateInstance(targetType, false);
// Find common members by name
var sourceMembers = from source in objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var targetMembers = from source in targetType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var commonMembers = targetMembers.Where(memberInfo => sourceMembers.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
// Remove unwanted members
commonMembers.RemoveWhere(x => x.Name.InList(excludeFields));
foreach (var memberInfo in commonMembers)
{
if (!((PropertyInfo)memberInfo).CanWrite) continue;
var targetProperty = typeof(T).GetProperty(memberInfo.Name);
if (targetProperty == null) continue;
var sourceProperty = myobj.GetType().GetProperty(memberInfo.Name);
if (sourceProperty == null) continue;
// Check source and target types are the same
if (sourceProperty.PropertyType.Name != targetProperty.PropertyType.Name) continue;
var value = myobj.GetType().GetProperty(memberInfo.Name)?.GetValue(myobj, null);
if (value == null) continue;
// Set the value
targetProperty.SetValue(targetInstance, value, null);
}
return (T)targetInstance;
}
I tried to use the Cast Extension (see https://stackoverflow.com/users/247402/stacker) in a situation where the Target Type contains a Property that is not present in the Source Type. It did not work, I'm not sure why. I refactored to the following extension that did work for my situation:
public static T Casting<T>(this Object source)
{
Type sourceType = source.GetType();
Type targetType = typeof(T);
var target = Activator.CreateInstance(targetType, false);
var sourceMembers = sourceType.GetMembers()
.Where(x => x.MemberType == MemberTypes.Property)
.ToList();
var targetMembers = targetType.GetMembers()
.Where(x => x.MemberType == MemberTypes.Property)
.ToList();
var members = targetMembers
.Where(x => sourceMembers
.Select(y => y.Name)
.Contains(x.Name));
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
value = source.GetType().GetProperty(memberInfo.Name).GetValue(source, null);
propertyInfo.SetValue(target, value, null);
}
return (T)target;
}
Note that I changed the name of the extension as the Name Cast conflicts with results from Linq. Hat tip https://stackoverflow.com/users/2093880/usefulbee
var obj = _account.Retrieve(Email, hash);
AccountInfoResponse accountInfoResponse = new AccountInfoResponse();
if (obj != null)
{
accountInfoResponse =
JsonConvert.
DeserializeObject<AccountInfoResponse>
(JsonConvert.SerializeObject(obj));
}
image description
I developed a Class ObjectChanger that contains the functions ConvertToJson, DeleteFromJson, AddToJson, and ConvertToObject. These functions can be used to convert a C# object to JSON which properties can then be removed or added accordingly. Afterwards the adjusted JSON object can simply be converted to a new object using ConvertToObject function. In the sample code below the class "AtoB" utilizes ObjectChanger in its GetAtoB() function:
using System.Collections.Generic;
using Newtonsoft.Json;
using Nancy.Json;
namespace YourNameSpace
{
public class A
{
public int num1 { get; set; }
public int num2 { get; set; }
public int num3 { get; set; }
}
public class B//remove num2 and add num4
{
public int num1 { get; set; }
public int num3 { get; set; }
public int num4 { get; set; }
}
/// <summary>
/// This class utilizes ObjectChanger to illustrate how
/// to convert object of type A to one of type B
/// by converting A to a Json Object, manipulating the JSON
/// and then converting it to object of type B
/// </summary>
public class AtoB
{
public dynamic GetAtoB()
{
A objectA = new A
{
num1 =1, num2 =2,num3 =3
};
//convert "objectA" to JSON object "jsonA"
dynamic jsonA = ObjectChanger.ConvertToJson(objectA);
//remove num2 from jsonA
ObjectChanger.DeleteFromJson(jsonA, "num2");
//add property num4 with value 4 to jsonA
ObjectChanger.AddToJson(jsonA, "num4", 4);
B objectB = ObjectChanger.ConvertToObject<B>(jsonA);
return objectB;
//note: Above DeleteFromJson not needed if the
//property(e.g "num2") doesn't exist in objectB
//the jsonA will still keep the num2 but when
//ConvertToObject is called the objectB will only get
//populated with the relevant fields.
}
}
public class ObjectChanger
{
/// <summary>
/// Converts a provided class to JsonObject
/// sample use: dynamic r = ObjectChanger.ConvertToJson(providedObj);
/// </summary>
public static dynamic ConvertToJson(dynamic providedObj)
{
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//https://stackoverflow.com/questions/7397207/json-net-error-self-referencing-loop-detected-for-type
return JsonConvert.DeserializeObject<System.Dynamic.ExpandoObject>
(JsonConvert.SerializeObject(providedObj,jss));
}
/// <summary>
/// Deletes Property from Json Object
/// sample use: dynamic r = ObjectChanger.ConvertToJson(providedObj);
/// ((IDictionary<string, object>)r).Remove("keyvalue");
/// </summary>
public static dynamic DeleteFromJson(dynamic providedObj, string keyvalue)
{
((IDictionary<string, object>)providedObj).Remove(keyvalue);
return providedObj;
}
/// <summary>
/// Adds Property to provided Json Object
/// </summary>
/// <param name="providedObj"></param>
/// <param name="key"></param>
/// <param name="keyvalue"></param>
/// <returns>Returns updated Object</returns>
public static dynamic AddToJson(dynamic providedObj, string key,
dynamic keyvalue)
{
((IDictionary<string, object>)providedObj).Add(key, keyvalue);
return providedObj;
}
/// <summary>
/// Converts provided object providedObj
/// to an object of type T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="providedObj"></param>
/// <returns></returns>
public static T ConvertToObject<T>(dynamic providedObj)
{
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(providedObj);
var c = serializer.Deserialize<T>(json);
return c;
}
}
}