通用方法 - 我可以避免在此代码中使用反射吗?

时间:2012-12-03 20:23:55

标签: c# generics

说我有以下方法:

private static void SetLastModifiedTimeUser<TEntity>(TEntity entity) where TEntity : class
{
    PropertyInfo propertyInfo;

    propertyInfo = entity.GetType().GetProperty("LastModifiedUser");
    if (propertyInfo != null)
        propertyInfo.SetValue(entity, IdentityHelper.UserName, null);
}

如您所见,该方法接受泛型类型。传递给此方法的每个类都将包含一个名为“LastModifiedUser”的属性。有没有办法可以在不使用反射的情况下访问此属性?我认为没有,但我想我会问。

4 个答案:

答案 0 :(得分:4)

是的,如果您的所有实体都具有LastModifiedUser属性,那么您可以使所有实体从基类继承,或实现某些接口,如

public interface IModifyable
{
    string LastModifiedUser { get; set; }
}

然后只需添加此约束(或使您的方法非通用,接受IModifyable

where TEntity : class, IModifyable

您的代码将如下所示:

private static void SetLastModifiedTimeUser<TEntity>(TEntity entity) 
   where TEntity : class, IModifyable
{
   entity.LastModifiedUser = IdentityHelper.UserName;
}

答案 1 :(得分:3)

让您的类继承自定义LastModifiedUser属性的接口。

public interface ILastModifiedUser
{
    public string LastModifiedUser { get; set; }
}

将方法声明更改为

private static void SetLastModifiedTimeUser(ILastModifiedUser entity)

答案 2 :(得分:1)

如果您无法修改所有类以实现通用接口,则可以使用dynamic

private static void SetLastModifiedTimeUser<TEntity>(TEntity entity) where TEntity : class
{
    dynamic d = entity;
    d.LastModifiedUser = IdentityHelper.UserName;
}

否则它就像Robert Harvey所示的那样简单。

答案 3 :(得分:1)

如果无法为对象添加界面,请考虑这种方法。

第一次遇到每个Type(TEntity)时,它会查找属性并获取属性的SetMethod。然后,在每次使用时,它都会调用方法。

var one = new EntityOne();
LastModifiedTimeUserSetter.Set(one);
Console.WriteLine(one.LastModifiedUser);

public static class LastModifiedTimeUserSetter
{
  public static void Set<TEntity>(TEntity entity)
  {
     var method = Properties.GetOrAdd(typeof (TEntity), GetSetMethod);
     var action = (Action<string>) Delegate.CreateDelegate(typeof (Action<string>), entity, method);
     action(IdentityHelper.UserName);
  }
  static MethodInfo GetSetMethod(Type type)
  {
     var prop = type.GetProperty("LastModifiedUser");
     if (prop == null)
        return null;
     return prop.GetSetMethod();
  }

  static readonly ConcurrentDictionary<Type, MethodInfo> Properties = new ConcurrentDictionary<Type, MethodInfo>();
}

更进一步

使用System.Reflection.Emit.MethodBuilder可以进一步提高效果。并构建一个获取Entity并设置属性的方法。

public static class LastModifiedTimeUserSetter
{
  public static void Set<TEntity>(TEntity entity)
  {
     var action = (Action<TEntity>) Properties.GetOrAdd(typeof(TEntity), CreateDynamicSetMethodDelegate);
     if(action != null)
        action(entity);
  }

  static Delegate CreateDynamicSetMethodDelegate(Type type)
  {
     return CreateDynamicSetMethod(type).CreateDelegate(GetActionType(type));
  }

  static DynamicMethod CreateDynamicSetMethod(Type typeWithProperty)
  {
     var methodBuilder = new DynamicMethod(
        "Dynamic_" + typeWithProperty.FullName + "_SetLastModifiedUser",
        typeof (void),
        new[] {typeWithProperty});
     EmitSimpleAssignmentMethod(methodBuilder,
                                GetIdentityHelperUserNameGetMethod(),
                                GetPropertySetMethod(typeWithProperty));
     return methodBuilder;
  }

  static MethodInfo GetIdentityHelperUserNameGetMethod()
  {
     return typeof(IdentityHelper).GetProperty("UserName").GetGetMethod();
  }

  static MethodInfo GetPropertySetMethod(Type type)
  {
     var prop = type.GetProperty("LastModifiedUser");
     if (prop == null)
        return null;
     return prop.GetSetMethod();
  }

  static void EmitSimpleAssignmentMethod(DynamicMethod methodBuilder, MethodInfo getMethod, MethodInfo setMethod)
  {
     var il = methodBuilder.GetILGenerator();
     il.Emit(OpCodes.Ldarg_0);
     il.EmitCall(OpCodes.Call, getMethod, null);
     il.EmitCall(OpCodes.Call, setMethod, null);
     il.Emit(OpCodes.Ret);
  }

  static Type GetActionType(Type type)
  {
     return typeof (Action<string>).GetGenericTypeDefinition().MakeGenericType(type);
  }

  static readonly ConcurrentDictionary<Type, Delegate> Properties = new ConcurrentDictionary<Type, Delegate>();
}

查看Article from MSDN magazine about XBOX Live