【问题标题】:C# What does using the [System.Diagnostics.Conditional] attribute on an attribute class actually doC# 在属性类上使用 [System.Diagnostics.Conditional] 属性实际上做了什么
【发布时间】:2018-03-18 02:22:01
【问题描述】:

我已经知道在方法上使用条件属性有一段时间了,但我刚刚发现它也可以在属性类上使用,所以我写了一些代码来测试它,但它没有达到预期的效果。

此 MSDN 页面显示如何在页面底部的属性类上使用条件属性:https://msdn.microsoft.com/en-us/library/4xssyw96%28v=vs.90%29.aspx

顺便说一句,我正在使用 Unity 引擎。我认为这无关紧要,但我可能猜到了。

这是我写的测试代码:

using System.Reflection;
using UnityEngine;

[System.Diagnostics.Conditional("UNITY_EDITOR")]
public class TestAttribute : System.Attribute
{
    public string text;

    public TestAttribute(string text)
    {
        this.text = text;
    }
}

public class NewBehaviourScript : MonoBehaviour
{
    [Test("This shouldn't exist on android")]
    public void Awake()
    {
#if UNITY_EDITOR
        Debug.Log("This only gets logged in the Unity Editor, not in an Android build");
#endif

        Debug.Log("Begin Attribute Test");
        {
            object[] attributes = typeof(NewBehaviourScript).GetMethod("Awake").GetCustomAttributes(true);
            for (int i = 0; i < attributes.Length; i++)
            {
                Debug.Log(attributes[i]);// This logs TestAttribute both in the editor and on android.
            }

            TestAttribute att = attributes[0] as TestAttribute;
            Debug.Log(att.text);// This logs "This shouldn't exist on android" both in the editor and on android.
        }
        Debug.Log("End Attribute Test");
        Debug.Log("");
        Debug.Log("Begin Method Test");
        {
            Method();// This only gets called in the Unity Editor, as expected from the conditional attribute.

            MethodInfo methodInfo = typeof(NewBehaviourScript).GetMethod("Method");
            Debug.Log(methodInfo);// this logs "void Method()" both in the editor and on android.
        }
        Debug.Log("End Method Test");
    }

    [System.Diagnostics.Conditional("UNITY_EDITOR")]
    public void Method()
    {
        Debug.Log("This shouldn't exist on android either");
    }
}

如果条件属性没有阻止 GetCustomAttributes() 获取测试属性,它实际上做了什么?

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    让我简化你的例子:

    [Conditional("DEBUG")]
    class MyAttribute : Attribute {}
    
    [MyAttribute]
    class MyClass {}
    

    Conditional 属性应用于Attribute 派生类型时,如果Conditional 属性的条件不成立,它会导致该属性从所有标记的符号 中删除。所以在上面的例子中,MyClass 仅在调试版本中被标记为MyAttribute;在 Release 版本中,DEBUG 符号未定义(通常至少是这样),因此编译器从 MyClass 的声明中删除了 [MyAttribute]。可以看到在使用反射的时候,尝试在debug和release构建中运行如下代码:

    private static void Main(string[] args)
    {
        var attribute = typeof(MyClass).GetCustomAttribute<MyAttribute>();
        Console.WriteLine(attribute == null ? "missing" : "exists");
    }
    

    这将在调试版本中打印“exists”,在发布版本中打印“missing”。但是,只有属性的 application 被移除;属性类本身仍然存在于编译的程序集中。这类似于 Conditional 对方法的工作方式:仅删除了方法调用,方法仍然存在并且可以通过反射调用。

    为什么会有用? JetBrains 在一篇博文中描述了一个用例(请参阅“JetBrains.Annotations NuGet 包”部分):他们有一个名为 JetBrains.Annotations 的 NuGet 包,其中包含有助于他们的工具 Resharper 分析 C# 代码的各种属性。但是,将这个 NuGet 包添加到您的项目中需要您将该程序集与您的产品一起提供,即使您只是真正将它用于编码;不是在运行时。所以他们所做的是:他们用Conditional 属性注释程序集中的所有属性。这会导致编译器在编译期间剥离属性;然后它注意到JetBrains.Annotations 从未被引用,从已编译的程序集中删除该引用。因此,您不必将 JetBrains 的组件与您的产品一起运送。

    但是,我不知道这是否适用于 Unity。众所周知,Unity 使用旧版本的 Mono 和 C# 编译器,因此可能存在阻止所有这些工作的错误。实际上,您的代码似乎表明这在 Unity 中确实无法正常工作。但是,如果您在独立的 .NET 应用程序中运行我或您的代码,它就可以工作。顺便说一句,您还可以使用较新版本的 C# 编译器来编译 Unity 然后可以引用的程序集;这应该允许您在 Unity 中解决此问题。

    2018 年 3 月 17 日更新:Unity 的最新版本现在具有更新的 Mono 运行时和更新的 C# 编译器(并将很快更新到 Microsoft 的 C# 编译器)。因此,任何关于条件编译属性的非标准行为都应该在最新版本的 Unity 中得到修复。

    【讨论】:

    • 我刚刚做了一个测试,发现 Unity 在将 MyAttribute 放在类上时实际上给出了预期的行为(该属性在调试模式之外不存在),但在将它放在方法或属性上时却没有就像我在我的例子中一样。
    • 我可能正在删除一个帖子,但需要注意的是,唤醒和更新等统一函数是通过反射调用的。
    【解决方案2】:

    根据this 文章,条件属性可防止编译器为不存在所述条件的void 返回函数发出 MSIL。

    我认为您第一次使用它是试图告诉系统如果不满足条件,则不应存在 TestAttribute 属性,这似乎不是它的用途。

    当您稍后获取方法 Method 的 MethodInfo 时,我认为编译器已生成对 Method 的调用,但它不应执行任何操作,因为没有要执行的代码。 (即该方法应立即返回。)

    您实际上并没有打电话给Method,只是获取有关它的信息,我认为这可能会误导您。

    【讨论】:

    • 查看我提供的链接的最后一部分。允许将条件属性放在属性类上,因此从逻辑上讲,当您将其放在那里时,应该能够假设它实现了某些目标。我将尝试调用方法信息并查看它是否记录任何内容。
    • 好的,所以调用方法信息确实会正常运行该方法。条件属性不会阻止该方法的存在,它只是去除实际的调用。但是,将条件属性放在类上会做什么呢?该类仍然存在,可以正常使用。
    • 您列出的文档仅涵盖 2008 年之前的 VS 版本。更高版本的 Conditional 信息没有说明在 Attribute 类上使用它的任何内容。可能是 1) 行为发生了变化,或者 2) 原始文档不正确。
    • 我认为,如果类只是在 Conditional 的基础上停止存在,将会引起很多麻烦。 (就像运行时无法找到在编译时可能已经知道的东西)。与此相比,仅 NOP 一个方法是微不足道的。
    • 我不这么认为。条件属性的元数据显示它仍然允许在类和方法上使用属性,如果我将条件属性放在非属性类上(如我的示例中的 NewBehaviourScript),编译器会抛出错误:“错误 CS1689:属性“System.Diagnostics.ConditionalAttribute”仅对方法或属性类有效”。可能你是对的,但如果是这样的话,我本来希望看到一个过时的警告或其他东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-07
    • 1970-01-01
    • 2013-06-02
    • 2020-11-21
    • 2015-02-26
    • 2017-06-29
    相关资源
    最近更新 更多