【问题标题】:An "elegant" way of identifying a field?识别领域的“优雅”方式?
【发布时间】:2010-04-29 17:40:18
【问题描述】:

我正在编写一个系统,它是程序员应用程序的基础,并且需要检测他们对某些数据的访问。我主要可以使用属性来做到这一点,如下所示:

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;)

类似的东西。相信我,我已经对此进行了非常彻底的研究,甚至直接分析字节码以检测访问由于可能存在间接等原因是不可靠的。我确实需要用户通知我。

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

这对我来说是个问题,因为实际上情况是这样的:我有以下课程:

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") 访问并更改字符串。

我们如何识别该字段?我只能想到字符串、整数和枚举(也就是整数)。但是:

  • 我们已经讨论过字符串的问题。
  • 整数是一种痛苦。它们甚至更糟,因为用户需要记住哪个 int 对应于哪个字段。重构同样困难。
  • 枚举(如NOT_SO_NICEZ,即SemiNiceClass的字段)便于重构,但它们要求用户为每个类(SemiNiceClass等)编写一个枚举,每个值都有一个类的领域。它很烦人。我不想让他们恨我;)

那么,我听到你问,为什么我们不能这样做(如下)?

semiNice.Write(semiNice.notSoNice).y = 0;

因为我需要知道正在访问什么字段,而semiNice.notSoNice 不识别字段。这是字段的值,而不是字段本身。

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

非常感谢您的建议。

提前致谢!

(另外,我无法为这个问题想出好的标签。如果您有更好的想法,请告诉我,我会编辑它们)


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

我不知道Write(x =>semiNice.y, 0) 是基于我为我的问题编写的类(SemiNiceClass 等)还是只是一个示例,但如果是前者,它与结构:SemiNiceClass 中没有 y 字段。你是说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&lt;,&gt; 中提取该信息。另外,为什么写semiNice.Write(x =&gt; semiNice.notSoNice.y, 50); 而不是semiNice.Write(() =&gt; semiNice.notSoNice.y, 50);,因为我们没有使用x 做任何事情?

谢谢。


编辑 #2: Hans Passant 的建议:用属性替换字段。

这是我原本打算做的,但重新编译不是一个选项。


编辑#3:Ben Hoffstein 的建议:动态代理;林福。

我已经对此进行了漫长而艰苦的研究,但由于相对复杂的原因,我无法使用它。解释太长了,但请放心:如果我可以使用它,我会的。它比我目前的解决方案要简洁得多。

【问题讨论】:

    标签: c# field notifications identifier magic-string


    【解决方案1】:

    使用表达式,例如写(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);
        }
    

    【讨论】:

    • 啊,完美。 +1,我将这个答案和 Akash 标记为答案。非常感谢:)
    • 糟糕。我只能标记一个作为答案。这是你的,因为你是第一个建议表达的人。
    【解决方案2】:

    您是否考虑过使用动态代理来拦截对目标类的所有调用,做您需要做的任何事情,然后将调用转发给目标?

    像 LinFu 这样的东西可能会成功:http://www.codeproject.com/KB/cs/LinFuPart1.aspx

    【讨论】:

    • 我已经研究了很多 LinFu 和其他 AOP 框架,以及非常强大的东西,例如 Profiling API。我真的很想使用它,但由于需要很长时间才能解释的原因,我不能使用它们。不过感谢您的建议:)
    【解决方案3】:

    以下代码支持 Refractoring,无需编写额外代码,但由于反射,它可能执行得不是很快,但它肯定会让您访问您想要的。

    你必须像下面这样玩一下表达式树,

    public class MagicStringsTest
    {
        public static void Main(string[] args)
        {
            SemiNiceClass semiNice = new SemiNiceClass();
            // The user wants to do:  semiNice.notSoNice.y = 50;
    
            semiNice.Write( t=>t.y , 50);
    
            Console.ReadLine();
        }
    }
    
    public class SemiNiceClass
    {
    
        public NotSoNiceClass notSoNice { get; set; }
        public int z { get; set; }
    
        public SemiNiceClass()
        {
            notSoNice = new NotSoNiceClass();
        }
    
        public void Write<R>(Expression<Func<NotSoNiceClass,R>> exp, R value)
        {
            if (exp.Body.NodeType == ExpressionType.MemberAccess)
            {
                MemberExpression e = exp.Body as MemberExpression;
                Console.WriteLine("Writing value for " + e.Member.Name 
                    + " of NotSoNiceClass");
                FieldInfo info = e.Member as FieldInfo;
    
                // value is set using reflection...
                info.SetValue(notSoNice, value);
            }
            else
            {
                // throw exception, expecting of type x=>x.y
            }
        }
    
    
    }
    

    【讨论】:

    • 谢谢 :) 我给了你 +1,但我只能将 1 个答案标记为 the 答案,我选择了 Hightechrider 的,因为他是第一个建议表达式的人地方。我希望我可以同时标记:/
    猜你喜欢
    • 2012-02-29
    • 1970-01-01
    • 1970-01-01
    • 2017-08-27
    • 2010-10-12
    • 2011-10-03
    • 2013-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多