我怎样才能让我的泛型方法工作
本文关键字:我的 泛型方法 工作 | 更新日期: 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,提拉克-谢谢你的帮助。你的两个答案都帮助我找出了问题所在,这就是为什么我给你的两个答案都投了赞成票,尽管这是"正确的"答案。