c# - 获取属性名称的 Linq 表达式和扩展方法

标签 c# .net data-binding poco system.componentmodel

我正在看这篇描述在 POCO 属性之间进行数据绑定(bind)的简单方法的帖子:Data Binding POCO Properties

Bevan 的评论之一包含一个简单的 Binder 类,可用于完成此类数据绑定(bind)。它非常适合我的需要,但我想实现 Bevan 提出的一些改进类(class)的建议,即:

  • 检查源和目标是 分配
  • 检查属性 由 sourcePropertyName 和 targetPropertyName存在
  • 检查类型兼容性 在两个属性之间

此外,鉴于通过字符串指定属性很容易出错,您可以改用 Linq 表达式和扩展方法。然后而不是写

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

你可以写

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

我很确定我可以处理前三个(尽管可以随意包含这些更改)但我不知道如何使用 Linq 表达式和扩展方法来编写代码而不使用属性名称字符串。

有什么建议吗?

这是在链接中找到的原始代码:

public static class Binder
{

    public static void Bind(
        INotifyPropertyChanged source,
        string sourcePropertyName,
        INotifyPropertyChanged target,
        string targetPropertyName)
    {
        var sourceProperty
            = source.GetType().GetProperty(sourcePropertyName);
        var targetProperty
            = target.GetType().GetProperty(targetPropertyName);

        source.PropertyChanged +=
            (s, a) =>
            {
                var sourceValue = sourceProperty.GetValue(source, null);
                var targetValue = targetProperty.GetValue(target, null);
                if (!Object.Equals(sourceValue, targetValue))
                {
                    targetProperty.SetValue(target, sourceValue, null);
                }
            };

        target.PropertyChanged +=
            (s, a) =>
            {
                var sourceValue = sourceProperty.GetValue(source, null);
                var targetValue = targetProperty.GetValue(target, null);
                if (!Object.Equals(sourceValue, targetValue))
                {
                    sourceProperty.SetValue(source, targetValue, null);
                }
            };
    }
}

最佳答案

以下将从 lambda 表达式返回属性名称作为字符串:

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));

更新

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);
    }    
}

关于c# - 获取属性名称的 Linq 表达式和扩展方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5252176/

相关文章:

asp.net - FormView ConvertEmptyStringToNull 和绑定(bind)

c# - 为什么即使我明确处理了 DataContext,我的连接也没有关闭?

.net - FOSS 智能字符识别 (ICR)

C# TCP 监听器

c# - 使用 c# 规范化幅度和相位

WCF 数据契约(Contract) DTO

C# 不可变类子类

c# - WPF - 如何保存没有任何 alpha channel 的 PNG?

c# - AutoCAD API 的 Application.AcadApplication 对象去了哪里?

c# - 如何将列表绑定(bind)到组合框?