How to pass objects properties into a generic function

感情迁移 提交于 2020-01-07 10:52:14

问题


I am trying to create a function that takes in a object property and returns back object property. How to get the property values into this specific Function, so that this function only takes takes in the object's specific property and not the entire object?

class Program
{

  public T MapFrom<T,V>(T SourceObject.property, V DestinationObject.Property) 
  //Not able to achieve this//
  {
    // Code to Map Property
  }


  // Here I want to specifically pass only one property of Object , not the entire one
  ProgramClassObject.MapFrom<Details,Person>(Details.FirstName,Person.FName)

  }  
}

// Class Containing Property
class Details
{
  public string FirstName { get; set;}           
}

// Class Containing Property
class Person
{
  public string FName { get; set;}           
}

回答1:


You can do it manually, or use some library (see comments, someone mentetioned about it).

If still want to implement yourself:

Prepare some useful Expression extensions:

public static B GetProperty<T, B>(this Expression<Func<T, B>> propertySelector, T target) where T : class
{
    if (target == null)
    {
        throw new ArgumentNullException("target");
    }

    if (propertySelector == null)
    {
        throw new ArgumentNullException("propertySelector");
    }

    var memberExpression = propertySelector.Body as MemberExpression;
    if (memberExpression == null)
    {
        throw new NotSupportedException("Only member expression is supported.");
    }

    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
    {
        throw new NotSupportedException("You can select property only. Currently, selected member is: " +
                                        memberExpression.Member);
    }

    return (B)propertyInfo.GetValue(target);
}

public static void SetProperty<T, B>(this Expression<Func<T, B>> propertySelector, T target, B value)
{
    SetObjectProperty(target, propertySelector, value);
}

public static void SetObjectProperty<T, B>(T target, Expression<Func<T, B>> propertySelector, object value)
{
    if (target == null)
    {
        throw new ArgumentNullException("target");
    }

    if (propertySelector == null)
    {
        throw new ArgumentNullException("propertySelector");
    }

    var memberExpression = propertySelector.Body as MemberExpression;
    if (memberExpression == null)
    {
        throw new NotSupportedException("Cannot recognize property.");
    }

    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
    {
        throw new NotSupportedException("You can select property only. Currently, selected member is: " + memberExpression.Member);
    }

    propertyInfo.SetValue(target, value);
}

MapFrom implementation:

public static void MapFrom<TObject, TTarget, TProp>(TObject source, TTarget dest,
    Expression<Func<TObject, TProp>> sourceSelector, Expression<Func<TTarget, TProp>> targetSelector)
          where TObject : class where TTarget : class
{
    var sourceValue = sourceSelector.GetProperty(source);
    targetSelector.SetProperty(dest, sourceValue);
}

Usage:

programClassObject.MapFrom(details, person, det => det.FirstName, per => per.FName);



回答2:


It sounds like what you're looking for is an expression. That's how some libraries like Entity Framework effectively "parse" the code that they're passed.

First, you can get the PropertyInfo from an expression through a method such as this. I'm going to explain how to use this below, so bear with me.

public static PropertyInfo GetPropertyInfo<TIn, TOut>(Expression<Func<TIn, TOut>> PropertyExpression)
{
    MemberExpression memberExpr;
    switch (PropertyExpression.Body.NodeType)
    {
        case ExpressionType.MemberAccess:
            memberExpr = (MemberExpression)PropertyExpression.Body;
            break;
        case ExpressionType.Convert:
            memberExpr = (MemberExpression)((UnaryExpression)PropertyExpression.Body).Operand;
            break;
        default:
            throw new NotSupportedException();
    }

    var property = (PropertyInfo)memberExpr.Member;
    return property;
}

Then, the method will become something like this. I've taken the liberty of ensuring the datatypes to be the same here, although you could change TOut to object if you'd prefer. I did this based on your name of MapFrom, which leads me to believe the properties are meant to "communicate" directly.

public T MapFrom<T, V, TOut>(Expression<Func<T, TOut>> Source, Expression<Func<V, TOut>> Destination)
{
    var sourceProperty = GetPropertyInfo<T, TOut>(Source);
    var destinationProperty = GetPropertyInfo<V, TOut>(Destination);

    // Use those
    // They're PropertyInfo instances, so it should be pretty easy to handle them however you would have expected to.
}

Once you've got all that,

var ret = MapFrom<Person, Details, string>(c => c.FName, c => c.FirstName);

The signature there could be cleaned up through the use of a generically typed master class, since you wouldn't have to specify any type arguments, and the string would be inferred. In a real-world situation, that's likely what you'd want to do, particularly since you appear to be, again, mapping values.



来源:https://stackoverflow.com/questions/25589863/how-to-pass-objects-properties-into-a-generic-function

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!