我正在编写一个系统,它是程序员应用程序的基础,并且需要检测它们对某些数据的访问。我通常可以使用属性来执行此操作,如下所示:

public class NiceClass {
    public int x { get; set; }
}

然后,我进入并调整getset访问器,以便它们适本地处理访问。但是,这要求用户(应用程序程序员)将其所有数据定义为属性。

如果用户要使用具有“普通”字段(与属性相反)的现有类,则无法检测到这些访问。例子:
public class NotSoNiceClass {
    public int y;
}

我无法检测到对y的访问。但是,我想允许使用预先存在的类。作为一种妥协,用户有责任在发生此类数据访问时通知我。例如:
NotSoNiceClass notSoNice;
...
Write(notSoNice.y, 0);  // (as opposed to notSoNice.y = 0;)

这样的事情。相信我,我已经对此进行了彻底的研究,甚至由于可能的间接访问等原因,甚至直接分析字节码来检测访问也不可靠。我确实需要用户通知我。

现在我的问题是:您能推荐一种“优雅”的方式来执行这些通知吗? (是的,我知道整个情况并非一开始就很“优雅”;我正在努力不使其变得更糟;))。你会怎么做?

这对我来说是个问题,因为实际上情况是这样的:我有以下类(class):
public class SemiNiceClass {
     public NotSoNiceClass notSoNice { get; set; }
     public int z { get; set; }
}

如果用户想要这样做:
SemiNiceClass semiNice;
...
semiNice.notSoNice.y = 0;

他们必须改为执行以下操作:
semiNice.Write("notSoNice").y = 0;

在那里Write将返回notSoNice的克隆,这也是我希望set访问器执行的操作。但是,使用字符串是很丑陋的:如果以后他们重构字段,则必须遍历Write("notSoNice")访问并更改字符串。

我们如何识别该领域?我只能想到字符串,整数和枚举(即再次是整数)。但:
  • 我们已经讨论了字符串问题。
  • Ints很痛苦。更糟糕的是,因为用户需要记住哪个int对应哪个字段。重构同样困难。
  • 枚举(例如NOT_SO_NICEZ,即SemiNiceClass的字段)易于重构,但是它们要求用户为每个类(SemiNiceClass等)编写一个枚举,并为该类的每个字段编写一个值。这很烦人。我不想他们恨我;)

  • 那么,为什么,我听到你问,我们不能这样做(如下)吗?
    semiNice.Write(semiNice.notSoNice).y = 0;
    

    因为我需要知道正在访问哪个字段,而semiNice.notSoNice不能标识一个字段。它是字段的值,而不是字段本身。

    叹。我知道这很丑。相信我 ;)

    我将不胜感激建议。

    提前致谢!

    (此外,对于这个问题,我无法提供好的标签。如果您有更好的主意,请告诉我,我会对其进行编辑)

    编辑#1: Hightechrider的建议:表达式。

    我不知道Write(x =>semiNice.y, 0)是基于我为我的问题写的类(SemiNiceClass等)还是仅作为示例,但如果是前者,则不匹配该结构:不存在y字段SemiNiceClass。您是说Write(x =>semiNice.notSoNice.y, 0)吗?

    我不确定您对我来说使用它的含义...我将发布我编写的代码:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace Test.MagicStrings {
    
        public class MagicStringsTest {
            public static void Main(string[] args) {
                SemiNiceClass semiNice = new SemiNiceClass();
                // The user wants to do:  semiNice.notSoNice.y = 50;
                semiNice.Write(x => semiNice.notSoNice.y, 50);
            }
        }
    
        public class SemiNiceClass {
    
            public NotSoNiceClass notSoNice { get; set; }
            public int z { get; set; }
    
            public SemiNiceClass() {
                notSoNice = new NotSoNiceClass();
            }
    
            public void Write(Func<object, object> accessor, object value) {
                // Here I'd like to know what field (y, in our example) the user wants to
                // write to.
            }
    
        }
    
        public class NotSoNiceClass {
            public int y;
        }
    
    }
    

    如何在Write中获取该信息?我找不到从Func<,>中提取该信息的任何方法。另外,由于我们不使用semiNice.Write(x => semiNice.notSoNice.y, 50);来做什么,为什么还要写semiNice.Write(() => semiNice.notSoNice.y, 50);而不是x呢?

    谢谢。

    编辑#2:汉斯·帕桑(Hans Passant)的建议:用属性替换字段。

    这是我本来打算做的,但是重新编译不是一种选择。

    编辑#3 :本·霍夫斯坦的建议:动态代理;林富

    我已经花了很长时间来研究这个问题,由于相对复杂的原因,我无法使用它。解释的时间太长了,但是请放心:如果我可以使用它,我会的。它比我当前的解决方案整洁得多。

    最佳答案

    使用表达式,例如写(x => semiNice.y,0)

    此技术通常用作避免魔术弦的一种方法。

    例如

        public void Write<T,U>(T source, Expression<Func<T, U>> lambda, U value)
        {
            var body = lambda.Body as MemberExpression;
            string memberName = body.Member.Name;
            if (body.Member.MemberType == MemberTypes.Field)
            {
                (body.Member as FieldInfo).SetValue(source, value);
            }
            else if (body.Member.MemberType == MemberTypes.Method)
            {
                (body.Member as MethodInfo).Invoke(source, new object[]{value});
            }
            Console.WriteLine("You wrote to " + memberName + " value " + value);
        }
    

    09-26 21:41