我怎样才能让我的泛型方法工作

本文关键字:我的 泛型方法 工作 | 更新日期: 2023-09-27 17:49:57

我正在为我的WPF viewModel基类编写一个小扩展,它允许我以一种比在getter中引发多个PropertyChanged事件的标准方法更干净的方式定义属性之间的依赖关系:

所以不是:

public int ThisProperty
{  
    get
    {
        thisProperty = value;
        RaisePropertyChangedEvent("ThisProperty");
        RaisePropertyChangedEvent("FirstDependentProperty");
        RaisePropertyChangedEvent("SecondDependentProperty");
    }
}

我希望能够在我的ViewModels构造函数中做这样的事情:

RegisterDependencies("This Property", 
    "FirstDependentProperty", "SecondDependentProperty");

我在我的viewModel中定义了以下方法(删除错误检查以减少代码量):

public void RegisterDependencies(string property, params string[] dependencies)
{
    foreach (string item in dependencies)
    {
        IList<string> deps;
        if (dependenciesList.TryGetValue(item, out deps))
        {
            if (!deps.Contains(property))
            {
                deps.Add(property);
            }
        }
        else
        {
            deps = new List<string>();
            deps.Add(property);
            dependenciesList[item] = deps;
        }
    }
}

我的视图模型使用以下方法订阅PropertyChanged事件:

void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    IList<string> dependencies;
    if (dependenciesList.TryGetValue(e.PropertyName, out dependencies))
    {
        foreach (string item in dependencies)
        {
            RaisePropertyChangedEvent(item);
        }
    }
}

这只是检查属性是否有任何依赖关系,如果有,也会引发PropertyChanged事件。到目前为止,这一切都运行得很好。

我真正想要的是使用lambda表达式而不是字符串,这样我就可以获得自动完成和更好的重构支持。我想要的是这样的内容:

RegisterDependencies<ViewModelType>(p => p.Property, 
    p => p.FirstDependency,
    p => p.SecondDependency); //and so on...

我重新定义了方法签名,看起来像这样:

public void RegisterDependencies<T>(Expression<Func<T, object>> property, 
    prams Expression<Func<T, object>>[] dependencies)
    {
    }

目前,该方法只是试图将表达式转换为字符串,这就是我要解开的地方。我不确定如何从表达式中获得属性的名称并将其转换为字符串。

Josh Smith的MVVM Foundation包含一个名为PropertyChangedObserver的类,其中有一段代码,我试图适应我的例子:

private static string GetPropertyName<T>(Expression<Func<T, object>> expression)
{
    var lambda = expression as LambdaExpression;
    MemberExpression memberExpression;
    if (lambda.Body is UnaryExpression)
    {
        var unaryExpression = lambda.Body as UnaryExpression;
        memberExpression = unaryExpression.Operand as MemberExpression;
    }
    else
    {
        memberExpression = lambda.Body as MemberExpression;
    }
    return memberExpression != null ? ((PropertyInfo)memberExpression.Member).Name : null;
}

这是在我更新的RegisterDependencies方法中调用的(这实际上是目前该方法的整个代码):

string propertyName = GetPropertyName(property);
foreach (Expression<Func<T, object>> expr in dependencies)
{
    string dependencyName = GetPropertyName(expr);
}

运行时,结果是XamlParseException。这很难调试,因为它不会弹出标准的异常窗口。Visual Studio的输出窗口提供了更多的信息,似乎最初的异常是一个InvalidCastException,虽然我不确定为什么这两个表达式是相同的类型。

有谁能帮我一下吗?

我怎样才能让我的泛型方法工作

根据Matti Virkkunen的评论,Member属性返回一个MemberInfo对象,该对象可以表示属性或字段。如果这是你的问题,不用担心;Name属性实际上是MemberInfo类的成员,因此不需要强制转换为PropertyInfo。

如果发生了其他事情,我们需要更多的信息来帮助;您可以将调用站点与您正在传递的实际lambda表达式发布吗?

似乎问题是在传递表达式给RegisterDependencies

下面(稍作修改的代码)成功运行。

    public static void Test()
    {
        var deps = RegisterDependencies((KeyValuePair<string,string> p) => p.Key, (KeyValuePair<string,string> p) => p.Value);
        foreach(var d in deps)
        {
            Console.WriteLine(d); // Prints Key, Then Value
        }
    }
    public static IEnumerable<string> RegisterDependencies<T>(Expression<Func<T, object>> property,    params Expression<Func<T, object>>[] dependencies)
    {
        var deps = new List<string>();
        deps.Add(GetPropertyName(property));
        foreach (var d in dependencies)
        {
            deps.Add(GetPropertyName(d));
        }
        return deps;
    }
    public static string GetPropertyName<T>(Expression<Func<T, object>> expression)
    {
        var lambda = expression as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }
        return memberExpression != null ? ((PropertyInfo)memberExpression.Member).Name : null;
    }

事实证明,不知怎么的,我已经设法输入错误的属性名称,例如在我想要的地方:

p => p.Quantity

我实际上输入了:

p => p.quantity

注意小写的q。由于我在构造函数中调用这个,我也可以访问所有的私有成员(所以我的类型有一个名为quantity的字段),这就是为什么这不会导致编译器抱怨,而是在运行时生成InvalidCastException,因为它显然不是一个属性。

@Phoog,提拉克-谢谢你的帮助。你的两个答案都帮助我找出了问题所在,这就是为什么我给你的两个答案都投了赞成票,尽管这是"正确的"答案。