C# 每当调用属性时调用方法

C# 每当调用属性时调用方法,c#,dynamic,reflection,attributes,C#,Dynamic,Reflection,Attributes,有没有一种方法可以向类中添加一个属性或某些东西,以便在调用该类的属性时调用该方法 基本上,我的类包含一个datarow,而不是访问单个列,比如:(datarow[“some_column_name”]),我想使用属性来访问它们,比如:(MyClass.some_column_name) 我当前的解决方法是使用反射动态传递列名作为参数,如下所示: public string some_column_name { get { retu

有没有一种方法可以向类中添加一个属性或某些东西,以便在调用该类的属性时调用该方法

基本上,我的类包含一个datarow,而不是访问单个列,比如:(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个属性的类,那么所有这些属性中都会包含上述代码片段,从而使类膨胀

下面的代码示例包含上述代码段的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;
}

您可以使用
[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;
}
dynamic x = new DynamicRow();
object val = x.some_column_name;
x.some_column_name = "some 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;
    }
}
然后你可以这样使用它:

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;
}
dynamic x = new DynamicRow();
object val = x.some_column_name;
x.some_column_name = "some value";

但我个人不太喜欢它,因为与第一种方法中使用的静态属性列表相比,您正在失去所有类型安全性(只有在运行时才会发现任何输入错误)。

下面是使用
PostSharp AOP
的代码的简单工作版本。当我们修剪
get
set
时,这里的结果将是TestProperty。在
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);

        }
    }
}

如果我没有找到其他解决方案,这肯定会清理分配的类,谢谢@我又增加了一个选择(虽然我认为第一个更好)我同意你提到的类型安全,第一个答案是感觉更好。如果我选择第二条答案路线,它将基本上与使用datarow而不使用[“”]相同。是的,所以我认为没有比第一条更好的方法了。为了实现安全,您必须手动列出所有有效的属性名称。使用诸如PostSharp之类的AOP工具,您可以消除样板文件GetValue和SetValue,但在这种情况下可能不值得。如果您想进一步简化getter/setter代码,请查看C#中的表达式体成员。这将把每个属性压缩成一行。