C# 在属性类上使用 [System.Diagnostics.Conditional] 属性实际上会做什么

Sil*_*Sin 2 c# unity-game-engine

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

此 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果条件属性不能阻止 GetCustomAttributes() 获取测试属性,那么它实际上会做什么?

Axe*_*ier 6

让我简化你的例子:

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

[MyAttribute]
class MyClass {}
Run Code Online (Sandbox Code Playgroud)

Conditional属性应用于派生类型时,如果属性的条件不成立,则会导致从所有标记符号Attribute中删除该属性。因此,在上面的示例中,仅在调试版本中标记为;在 Release 版本中,该符号未定义(通常是这样),因此编译器会从. 您可以看到,使用反射时,尝试在调试和发布版本中运行以下代码:ConditionalMyClassMyAttributeDEBUG[MyAttribute]MyClass

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

这将在调试版本中打印“存在”,在发布版本中打印“缺失”。但是,仅删除了该属性的应用;属性类本身仍然存在于已编译的程序集中。这类似于Conditional方法的工作方式:仅删除方法调用,方法仍然存在并且可以通过反射来调用。

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

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

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