【问题标题】:Call a method whenever a property is called每当调用属性时调用方法
【发布时间】:2018-10-16 20:45:14
【问题描述】:

有没有办法给一个类添加一个属性或一些东西,每当该类的属性被调用时,一个方法就会被调用?

基本上我有包含数据行的类,而不是访问单个列,例如:(DataRow["some_column_name"]) 我想使用属性访问它们,而不是:(MyClass.some_column_name)。

我目前的工作是使用反射来动态传递列名作为参数,如下所示:

public string some_column_name
    {
        get
        {

            return (string)GetValue(MethodBase.GetCurrentMethod().Name);
        }
        set
        {
            SetValue(MethodBase.GetCurrentMethod().Name, value);
        }
    }

上述方法的问题是,如果我有一个包含 10 个属性的类,所有这些属性都会在其中包含上述代码 sn-p 使类膨胀。

下面的代码示例包含上面代码sn-p的get和set value方法,这些方法将在基类中:

private DataRow some_table;

protected object GetValue(string columnName)
{
    return some_table[columnName.Substring(columnName.IndexOf('_') + 1)];
}

protected object SetValue(string columnName, object value)
{
    return  some_table[columnName.Substring(columnName.IndexOf('_') + 1)] = value;
}

【问题讨论】:

    标签: c# dynamic reflection attributes


    【解决方案1】:

    您可以使用[CallerMemberName] 属性。如果你像这样用这个属性装饰方法参数:

    protected object GetValue([CallerMemberName] string columnName = null)
    {
        return some_table[columnName.Substring(columnName.IndexOf('_') + 1)];
    }
    
    protected object SetValue(object value, [CallerMemberName] string columnName = null)
    {
        return  some_table[columnName.Substring(columnName.IndexOf('_') + 1)] = value;
    }
    

    编译器将使用调用者成员(方法)名称而不是默认的空值。所以你的财产变得公正:

    public string some_column_name
    {
        get
        {
            // current method name ("some_column_name") will be passed 
            // as "column" parameter
            return (string)GetValue();
        }
        set
        {
            // same here
            SetValue(value);
        }
    }
    

    另一种选择可能是从DynamicObject继承:

    public class DynamicRow : DynamicObject {
        public override bool TryGetMember(GetMemberBinder binder, out object result) {
            // this will be called when you access dynamic property
            // like x.some_column_name
            result = GetValue(binder.Name);
            return true;
        }
    
        public override bool TrySetMember(SetMemberBinder binder, object value) {
            // this will be called when you assign dynamic property
            // like x.some_column_name = "some value"
            SetValue(binder.Name, value);
            return true;
        }
    
        private DataRow some_table;
    
        protected object GetValue(string columnName) {
            return some_table[columnName.Substring(columnName.IndexOf('_') + 1)];
        }
    
        protected object SetValue(string columnName, object value) {
            return some_table[columnName.Substring(columnName.IndexOf('_') + 1)] = value;
        }
    }
    

    那么你可以这样使用它:

    dynamic x = new DynamicRow();
    object val = x.some_column_name;
    x.some_column_name = "some value";
    

    但我个人不太喜欢它,因为与在第一种方法中使用静态属性列表相比,您将失去所有类型安全性(任何错字只会在运行时被捕获)。

    【讨论】:

    • 如果我没有找到不同的解决方案,这肯定会清理分配的课程,谢谢!
    • @GysRademeyer 我又添加了一个替代方案(尽管我认为第一个更好)
    • 我同意你提到的类型安全,第一个答案是感觉更好。如果我走第二个答案路线,它基本上与使用没有 [""] 的数据行相同。
    • 是的,所以我认为没有比第一个更好的方法了。为了安全起见,您无论如何都必须手动列出所有有效的属性名称。使用 PostSharp 之类的 AOP 工具,您可以消除样板 GetValue 和 SetValue,但在这种情况下可能不值得。
    • 如果您想进一步简化 getter/setter 代码,请查看 C# 中的表达式主体成员。这应该将每个属性压缩成一个单行。
    【解决方案2】:

    以下是使用PostSharp AOP 的代码的简单工作版本。当我们修剪get_set_ 时,此处的结果将是TestPropertyOnInvoke 可以进行更多的处理,MethodInterceptionArgs 可用于对调用者进行大量修改,然后我们可以选择从 PostSharp 调用本身返回或进一步执行该方法。你也可以计划使用像OnMethodBoundaryAspect这样的AOP函数,它可以在方法成功或失败时执行

    namespace TestAOP
    {
        class Program
        {
            static void Main(string[] args)
            {
               var t = new Test();
    
               var x = t.TestProperty;
    
               Console.WriteLine(x);
    
               Console.Read();
            }
        }
    
        public class Test
        {
            private string _test = "InitialValue";
    
            [CustomProperty]
            public string TestProperty
            {
                get => _test;
                set => _test = value;
            }
        }
    
        [Serializable]
        public sealed class CustomPropertyAttribute : MethodInterceptionAspect
        {
            public override void OnInvoke(MethodInterceptionArgs args)
            {
               args.ReturnValue = args.Method.Name.Substring(args.Method.Name.IndexOf("_", StringComparison.Ordinal) + 1);
    
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-01-27
      • 2012-10-24
      • 1970-01-01
      • 2016-09-04
      • 1970-01-01
      • 2015-12-03
      • 2018-09-05
      • 2011-05-13
      • 2023-03-30
      相关资源
      最近更新 更多