每当调用属性时调用方法

5

有没有一种方法可以向类添加属性或其他内容,以便每当调用该类的属性时都会调用一个方法?

基本上,我有包含数据行的类,而不是像这样访问单个列:(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个属性,所有这些属性都将有上述代码片段膨胀该类。
以下代码示例包含上述代码片段的获取和设置值方法,这些方法将在基类中:
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;
}
2个回答

4
您可以使用[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";

但是我个人并不太喜欢它,因为与第一种方法使用静态属性列表相比,你失去了所有类型安全性(任何打字错误都只能在运行时捕获)。


1
@GysRademeyer 我添加了另一种选择(尽管我认为第一个更好)。 - Evk
我同意你所提到的类型安全性,第一个答案更好。如果我选择第二个答案,基本上就像直接使用未经过[""]处理的DataRow一样。 - Gys Rademeyer
1
如果你想进一步简化getter/setter代码,请查看C#中的表达式主体成员。这将把每个属性压缩成一个一行代码。 - Ashley Pillay
@AshleyPillay 这是个好主意,不幸的是我只能使用C#6,而这些表达式需要C#7。 - Gys Rademeyer
@Evk 之前有人向我提到了AOP,我会去看一下,看看它是否是我需要的。等我看完后再告诉你。谢谢! - Gys Rademeyer
显示剩余2条评论

0
以下是使用PostSharp AOP的代码的简单工作版本。在这里结果会是TestProperty,因为我们删除了get_set_。在OnInvoke中可以进行更多的处理,MethodInterceptionArgs 可以用来对调用者进行许多修改,然后我们可以选择从PostSharp调用本身返回或进一步执行该方法。您还可以计划使用AOP函数,例如OnMethodBoundaryAspect,它可以在方法成功或失败时执行。
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);

        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接