我有一个具有属性的属性,而该属性又有Func<object, object>
,我希望在属性更改时执行该函数(使用更新的属性的值为in T
)。最简单的方法是什么?
注意:我知道属性 static 的事实并不是为了在他们的受让人更改/调用时执行。我只需要让它尽可能接近我创建的原型。
一些代码:
using System;
using System.Windows;
namespace AnnotatedBinding
{
public class AnnotatedPropertyAttribute: Attribute
{
// static
public AnnotatedPropertyAttribute(Func<object, object> evaluator)
{
Evaluator = evaluator;
}
public Func<object, object> Evaluator
{
get; private set;
}
}
public class Test
{
[AnnotatedProperty(Test.TestEvaluator)] // not compiling!, guess it's fixable by passing in a member info and then calling Reflection Invoke?
public string TestProperty
{
get; set;
}
public static Func<object, object> TestEvaluator = (x) => MessageBox.Show(x.ToString());
}
public class Shell
{
public void Run()
{
var test = new Test();
test.TestProperty = "blah";// I want my message box here
test.TestProperty = "blah";// and I don't want it here
}
}
}
答案 0 :(得分:2)
TestProperty
上的属性无法编译,因为不允许委托作为属性参数。有关允许哪些类型的详细信息,请参阅this answer from Eric Lippert。
关于使用反射的变通方法:您当然可以指定拥有该方法的类型,以及属性中的方法名称,因为System.Type
和string
是有效的属性参数类型。像这样:
[AnnotatedProperty(typeof(Test), "TestEvaluator")]
public string TestProperty { get; set; }
但是,在设置属性时,仍然无法对委托做任何事情。属性只是您可以在运行时使用反射(更具体地使用MemberInfo.GetCustomAttributes(...)
)读取的元数据,分析它们并基于属性值执行任何操作。这一切都需要手动完成。遗憾的是,.NET框架不提供基于应用于成员的属性自动执行某些操作的功能。这将使财产变更通知的生活变得更加容易。
因此您必须手动实现属性的处理。这意味着,实现get
和set
访问器,检查属性是否应用于该属性,确定应该执行的委托,并使用反射来执行它。当然,这没有意义,因为你宁愿在setter中添加对方法的调用。
<强> TL; DR:强>
可能的解决方案:您应该查看PostSharp,这是一个支持.NET中面向方面编程的库。它可用于在编译后将样板代码注入方法或其他成员。它通过分析您的MSIL代码并搜索所谓的&#34;方面来实现这一点。 (实际上属于你的属性)。如果找到,它将修改属性指定的MSIL。您必须从PostSharp基本属性/方面派生属性,然后覆盖适当的方法。在您的情况下,您必须从LocationInterceptionAspect
派生,然后覆盖OnSetValue(...)
方法。在此方法中,您将使用属性参数(如上所述)确定委托,然后使用反射调用它。 "Intercepting Properties and Fields" in the PostSharp documentation非常好地介绍了如何做到这一点。
我认为你最终会得到这样的结果:
public class ExecuteDelegateOnPropertySetAspect : LocationInterceptionAspect
{
public ExecuteDelegateOnPropertySetAspect(Type methodOwner, string methodName, object[] arguments)
{
this.MethodOwner = methodOwner;
this.MethodName = methodName;
this.Arguments = arguments;
}
public Type MethodOwner { get; set; }
public string MethodName { get; set; }
public object[] Arguments { get; set; }
public override void OnSetValue(LocationInterceptionArgs args)
{
// get method with the specified name from the specified owner type
MethodInfo method = this.MethodOwner.GetMethod(this.MethodName);
// method must be static, otherwise we would need an instance to call it
if (method != null && method.IsStatic)
{
if (method.GetParameters().Length == this.Arguments.Length)
{
// call the method with the given arguments
method.Invoke(null, this.Arguments);
}
}
// execute the original setter code
args.ProceedSetValue();
}
}
在您的代码中,您可以将此方面应用于您的属性:
public class Test
{
public static void TestMethod(string someMessage)
{
MessageBox.Show(someMessage);
}
[ExecuteDelegateOnPropertySetAspect(typeof(Test), "TestMethod", new object[] { "Hello world!" })]
public string TestProperty { get; set; }
}
请注意,我省略了大部分错误和空值检查以保持简单和简短。
答案 1 :(得分:0)
您似乎误解了C#中的属性概念 属性具有getter和setter函数。当您设置属性或获取其值时,它们将自动执行。
所以您需要做的就是将属性的set函数更改为:
public class Test
{
private string _testProperty;
private bool testPropertyIsSet = false;
public string TestProperty
{
get { return this._testProperty; }
set
{
_testProperty = value;
if (!testPropertyIsSet)
{
// Do something here when your property gets set for the first time
}
testPropertyIsSet = true;
}
}
}
然后叫它:
public void Run()
{
var test = new Test();
test.TestProperty = "blah";
test.TestProperty = "blah2";
}