Выражения Linq и методы расширения для получения имени свойства - PullRequest
4 голосов
/ 10 марта 2011

Я просматривал этот пост, в котором описан простой способ связывания данных между свойствами POCO: Связывание данных со свойствами POCO

В одном из комментариев Бевана содержался простой класс Binder, который можетиспользоваться для выполнения такой привязки данных.Это прекрасно работает для того, что мне нужно, но я хотел бы реализовать некоторые предложения, которые Беван сделал для улучшения класса, а именно:

  • Проверка того, что источник и цель назначены
  • Проверкачто свойства, идентифицированные sourcePropertyName и targetPropertyName, существуют
  • Проверка совместимости типов между этими двумя свойствами

Кроме того, учитывая, что указание свойств в виде строки подвержено ошибкам, вы можете использовать выражения Linq иметоды расширения вместо.Тогда вместо написания

Binder.Bind( source, "Name", target, "Name")

вы могли бы написать

source.Bind( Name => target.Name);

Я почти уверен, что справлюсь с первыми тремя (хотя не стесняйтесь включать эти изменения), но я не имею понятиякак использовать выражения Linq и методы расширения, чтобы иметь возможность писать код без использования строк с именами свойств.

Любые советы?

Вот оригинальный код, который можно найти по ссылке:

** 1026

Ответы [ 5 ]

12 голосов
/ 10 марта 2011

Следующее вернет имя свойства в виде строки из лямбда-выражения:

public string PropertyName<TProperty>(Expression<Func<TProperty>> property)
{
  var lambda = (LambdaExpression)property;

  MemberExpression memberExpression;
  if (lambda.Body is UnaryExpression)
  {
    var unaryExpression = (UnaryExpression)lambda.Body;
    memberExpression = (MemberExpression)unaryExpression.Operand;
  }
  else
  {
    memberExpression = (MemberExpression)lambda.Body;
  }

  return memberExpression.Member.Name;
}

Использование:

public class MyClass
{
  public int World { get; set; }
}

...
var c = new MyClass();
Console.WriteLine("Hello {0}", PropertyName(() => c.World));

UPDATE

public static class Extensions
{
    public static void Bind<TSourceProperty, TDestinationProperty>(this INotifyPropertyChanged source, Expression<Func<TSourceProperty, TDestinationProperty>> bindExpression)
    {
        var expressionDetails = GetExpressionDetails<TSourceProperty, TDestinationProperty>(bindExpression);
        var sourcePropertyName = expressionDetails.Item1;
        var destinationObject = expressionDetails.Item2;
        var destinationPropertyName = expressionDetails.Item3;

        // Do binding here
        Console.WriteLine("{0} {1}", sourcePropertyName, destinationPropertyName);
    }

    private static Tuple<string, INotifyPropertyChanged, string> GetExpressionDetails<TSourceProperty, TDestinationProperty>(Expression<Func<TSourceProperty, TDestinationProperty>> bindExpression)
    {
        var lambda = (LambdaExpression)bindExpression;

        ParameterExpression sourceExpression = lambda.Parameters.FirstOrDefault();
        MemberExpression destinationExpression = (MemberExpression)lambda.Body;

        var memberExpression = destinationExpression.Expression as MemberExpression;
        var constantExpression = memberExpression.Expression as ConstantExpression;
        var fieldInfo = memberExpression.Member as FieldInfo;
        var destinationObject = fieldInfo.GetValue(constantExpression.Value) as INotifyPropertyChanged;

        return new Tuple<string, INotifyPropertyChanged, string>(sourceExpression.Name, destinationObject, destinationExpression.Member.Name);
    }
}

Использование:

public class TestSource : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string Name { get; set; }        
}

public class TestDestination : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string Id { get; set; }    
}

class Program
{        
    static void Main(string[] args)
    {
        var x = new TestSource();
        var y = new TestDestination();

        x.Bind<string, string>(Name => y.Id);
    }    
}
1 голос
/ 20 июня 2013

Этот вопрос очень похож на: Получение имени свойства из лямбда-выражения

(перекрестная публикация ответа от https://stackoverflow.com/a/17220748/1037948)

Не знаюзнать, нужно ли вам привязываться к «под-свойствам», но проверка lambda.Body для Member.Name вернет только свойство «final», а не «полностью определенное» свойство.

ex) o => o.Thing1.Thing2приведет к Thing2, а не Thing1.Thing2.

Это проблематично при попытке использовать этот метод для упрощения EntityFramework DbSet.Include(string) с перегрузками выражений.

Таким образом, вы можете «обмануть» ивместо этого разберите Expression.ToString.В моих тестах производительность казалась сопоставимой, поэтому, пожалуйста, исправьте меня, если это плохая идея.

Метод расширения

/// <summary>
/// Given an expression, extract the listed property name; similar to reflection but with familiar LINQ+lambdas.  Technique @via https://stackoverflow.com/a/16647343/1037948
/// </summary>
/// <remarks>Cheats and uses the tostring output -- Should consult performance differences</remarks>
/// <typeparam name="TModel">the model type to extract property names</typeparam>
/// <typeparam name="TValue">the value type of the expected property</typeparam>
/// <param name="propertySelector">expression that just selects a model property to be turned into a string</param>
/// <param name="delimiter">Expression toString delimiter to split from lambda param</param>
/// <param name="endTrim">Sometimes the Expression toString contains a method call, something like "Convert(x)", so we need to strip the closing part from the end</pa ram >
/// <returns>indicated property name</returns>
public static string GetPropertyName<TModel, TValue>(this Expression<Func<TModel, TValue>> propertySelector, char delimiter = '.', char endTrim = ')') {

    var asString = propertySelector.ToString(); // gives you: "o => o.Whatever"
    var firstDelim = asString.IndexOf(delimiter); // make sure there is a beginning property indicator; the "." in "o.Whatever" -- this may not be necessary?

    return firstDelim < 0
        ? asString
        : asString.Substring(firstDelim+1).TrimEnd(endTrim);
}//--   fn  GetPropertyNameExtended

(Проверка разделителя может быть даже излишней)

0 голосов
/ 05 сентября 2015

объявление:

    class Foo<T> {
            public string Bar<T, TResult>(Expression<Func<T, TResult>> expersion)
            {
                var lambda = (LambdaExpression)expersion;
                MemberExpression memberExpression;
                if (lambda.Body is UnaryExpression)
                {
                    var unaryExpression = (UnaryExpression)lambda.Body;
                    memberExpression = (MemberExpression)unaryExpression.Operand;
                }
                else
                {
                    memberExpression = (MemberExpression)lambda.Body;
                }

                return memberExpression.Member.Name;
            }
    }

Использование:

    var foo = new Foo<DummyType>();
    var propName = foo.Bar(d=>d.DummyProperty)
    Console.WriteLine(propName); //write "DummyProperty" string in shell
0 голосов
/ 13 марта 2013

var pr = typeof (CCategory) .GetProperties (). Select (i => i.Name) .ToList ();;

0 голосов
/ 10 марта 2011

Это, вероятно, больше или не совсем то, что вы просили, но я сделал нечто похожее для обработки отображения свойства между двумя объектами:

public interface IModelViewPropagationItem<M, V>
    where M : BaseModel
    where V : IView
{
    void SyncToView(M model, V view);
    void SyncToModel(M model, V view);
}

public class ModelViewPropagationItem<M, V, T> : IModelViewPropagationItem<M, V>
    where M : BaseModel
    where V : IView
{
    private delegate void VoidDelegate();

    public Func<M, T> ModelValueGetter { get; private set; }
    public Action<M, T> ModelValueSetter { get; private set; }
    public Func<V, T> ViewValueGetter { get; private set; }
    public Action<V, T> ViewValueSetter { get; private set; }

    public ModelViewPropagationItem(Func<M, T> modelValueGetter, Action<V, T> viewValueSetter)
        : this(modelValueGetter, null, null, viewValueSetter)
    { }

    public ModelViewPropagationItem(Action<M, T> modelValueSetter, Func<V, T> viewValueGetter)
        : this(null, modelValueSetter, viewValueGetter, null)
    { }

    public ModelViewPropagationItem(Func<M, T> modelValueGetter, Action<M, T> modelValueSetter, Func<V, T> viewValueGetter, Action<V, T> viewValueSetter)
    {
        this.ModelValueGetter = modelValueGetter;
        this.ModelValueSetter = modelValueSetter;
        this.ViewValueGetter = viewValueGetter;
        this.ViewValueSetter = viewValueSetter;
    }

    public void SyncToView(M model, V view)
    {
        if (this.ViewValueSetter == null || this.ModelValueGetter == null)
            throw new InvalidOperationException("Syncing to View is not supported for this instance.");

        this.ViewValueSetter(view, this.ModelValueGetter(model));
    }

    public void SyncToModel(M model, V view)
    {
        if (this.ModelValueSetter == null || this.ViewValueGetter == null)
            throw new InvalidOperationException("Syncing to Model is not supported for this instance.");

        this.ModelValueSetter(model, this.ViewValueGetter(view));
    }
}

Это позволяет вам создать экземпляробъект, а затем используйте «SyncToModel» и «SyncToView» для перемещения значений вперед и назад.Следующая часть, которая идет с этим, позволяет вам сгруппировать несколько таких вещей и перемещать данные назад и вперед одним вызовом:

public class ModelViewPropagationGroup<M, V> : List<IModelViewPropagationItem<M, V>>
    where M : BaseModel
    where V : IView
{
    public ModelViewPropagationGroup(params IModelViewPropagationItem<M, V>[] items)
    {
        this.AddRange(items);
    }

    public void SyncAllToView(M model, V view)
    {
        this.ForEach(o => o.SyncToView(model, view));
    }

    public void SyncAllToModel(M model, V view)
    {
        this.ForEach(o => o.SyncToModel(model, view));
    }
}

Использование будет выглядеть примерно так:Надеюсь, это поможет!

...