【问题标题】:Change code based on existing attribute?根据现有属性更改代码?
【发布时间】:2011-04-19 10:26:17
【问题描述】:

我正在编写一个在 .net 和 silverlight 之间共享的库。我有几个地方正在这样做,以满足 silverlight 反序列化(无法访问私有成员):

    [DataMember (IsRequired = true)]
    public Object MyProperty { get; 
#if SILVERLIGHT
        internal    
#else
        private 
#endif
         set; }

我知道这方面的规则,如果设置器是私有的并且定义了 SILVERLIGHT,那么设置器应该是内部的。

我可以使用像 postsharp 这样的面向方面的框架来帮助我减少此代码,这样我就不需要指定任何内容并且它会检查属性,如果它具有 DataMember 属性并且 setter 是私有的,那么将改为内部设置?

或者我可以使用其他一些技术吗?

编辑

似乎有些混乱。我的目标是完全避免使用编译器指令,但仍然拥有使用 .net 中的私有成员生成的代码以及可以由 Silverlight 中的DataContractDeserializer 设置的成员,不能访问私有成员。如果可能的话,我想在 silverlight 构建中自动修改属性,使其成为内部属性,同时在源代码中除了 DataMember 属性之外没有任何其他属性。

理想情况下,我认为解决方案类似于:

  • 编写一个检查每个属性或字段的切面。
  • 如果属性/字段具有[DataMember] 属性,则
    • 如果 silverlight 编译器指令存在,则
      • 如果 setter 是私有的,让它成为内部的(对于属性),或者如果它被声明为私有的,让它成为内部的(对于字段)

但我不确定使用 post sharp 之类的工具可以做到哪些方面。

【问题讨论】:

  • 这样做会导致潜在问题吗?这些成员不能在课堂外访问吗?内部 = 对程序集公开,而私有意味着只有类可以访问。
  • @DustinDavis 因为 same 代码由 .net 和 silverlight 编译,任何使用内部变量的地方都不会在 .net 构建中编译,就像在那个构建中一样它是私人的。开发人员必须在 #if SILVERLIGHT 编译器指令中使用该属性,以免引起问题,他们可以这样做,但不太可能
  • 很多人尝试这样做,但我不知道为什么。只需使用共享代码(尽可能多地)使用不同的程序集。你正在以一种可怕的方式编写代码。
  • @Sam 您需要使用 DTO,因此您不必拥有私人/内部成员。 DTO 是数据的轻量级版本,并且总是(通常)使用公共属性。不要传递您的模型或业务对象。有关属性注入的示例,请参见下面的答案
  • 始终使用 DTO 传输数据/对象,然后根据需要使用它们。如果这需要构建业务对象,那就这样吧。然后,您将您的服务与您想要的客户分离。您的服务和客户端不需要了解彼此的任何信息,除了公共接口(使用 DTO),它只有确切需要的成员,仅此而已。

标签: c# shared-libraries aop postsharp


【解决方案1】:

其他答案要么攻击问题的优点,要么提出不直接回答问题的替代方法。问题是是否有办法在编译后更改具有 DataMember 属性的属性的 setter 的可见性,以支持两个版本(.NET 和 Silverlight)。

我怀疑 PostSharp SDK 会支持这一点。然而,这是我在开发Afterthought 时必须解决的问题,因为我需要更改 C# 编译器生成的匿名静态委托的可见性(通常是私有的,直到我将它们设为内部)。 Afterthought 本身目前不直接支持您的方案,但它利用了开源的 Microsoft CCI 库,它支持。 IL Mutator 示例展示了如何使用 CCI 库加载已编译的程序集并通过创建可变副本对其进行修改。该示例实际上比您的场景复杂得多,因为您不会修改 IL,只是修改 setter 的可见性。

这是从 CCI 中的 mutator 中更改方法可见性的示例:

public override MethodDefinition Mutate(MethodDefinition methodDef)     
{
    // Automatically make all private static methods to have internal scope
    if (methodDef.IsStatic && methodDef.Visibility == TypeMemberVisibility.Private)
        methodDef.Visibility = TypeMemberVisibility.Assembly;

这是来自 Afterthought 修改器的略微简化的 excerpt。同一个类中还有一些示例说明如何确定方法是否为 setter(以 set_、HideBySig 等开头)。您只需要创建一个 mutator,覆盖此示例中的方法,验证该方法是一个属性设置器,在包含的属性定义上具有 DataMember 属性,然后更改可见性。

【讨论】:

  • 谢谢。这直接解决了我的问题,并提供了一种方法来做我想做的事。非常感谢!
【解决方案2】:

简单的答案是否定的。 PostSharp 是一个 Post-Compile 框架,因此您不能使用编译器指令(就像您在问题中尝试做的那样)。您可以使用 PostSharp 来

  1. 使用所需的访问器将属性引入类(在设计时不会让您访问) 可以在设计时将指令应用于方面以确定要注入的属性
  2. 或者使用反射来改变访问器(我不认为你可以这样做)

另一种方法是使用 T4 模板为您生成这些类

编辑:属性注入示例

[Serializable]
    [MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict)]
    public class PropInj : InstanceLevelAspect
    {
#if SILVERLIGHT
        [IntroduceMember(OverrideAction = MemberOverrideAction.Ignore, IsVirtual=true, Visibility=Visibility.FamilyAndAssembly)]
        public string MyProperty { get; set; }
#else
        [IntroduceMember(OverrideAction = MemberOverrideAction.Ignore, IsVirtual = true, Visibility = Visibility.Private)]
        public string MyProperty { get; set; }
#endif
    }

    [PropInj]
    public class test
    {
        //public int MyProperty { get; set; }

        public test()
        {

        }

    }

但您确实需要重新考虑您的设计。

【讨论】:

  • 不,我想你误会了。我想摆脱编译器指令。我想使用 post sharp 之类的东西来为 Silverlight 生成不同于 .net 的属性,这样这些编译器指令就不会乱扔代码。
  • 从答案听起来我可能能够根据应用于方面的编译器指令引入不同的 set 访问器,这将使我能够将编译器指令放在一个地方并拥有我的所有[DataMember] 属性/字段根据指令进行更改
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-22
  • 1970-01-01
  • 2013-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多