ChatGPT解决这个技术问题 Extra ChatGPT

#if DEBUG vs. Conditional("DEBUG")

在大型项目中哪个更好用,以及为什么:

#if DEBUG
    public void SetPrivateValue(int value)
    { ... }
#endif

或者

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
有关此问题的一些想法,请参见 blogs.msdn.com/b/ericlippert/archive/2009/09/10/…
你也可以使用它: if (Debugger.IsAttached) {...}
Unity 开发人员注意事项:DEBUG 表示在编辑器中或在开发版本中。 forum.unity.com/threads/…
实际上,不需要archive.org,@EricLippert 将他的博客移至此处:(ericlippert.com/2009/09/10/…)

S
Sheridan

这真的取决于你要做什么:

#if DEBUG:这里的代码在发布时甚至不会到达 IL。

[Conditional("DEBUG")]:此代码将到达 IL,但是对方法的调用将被省略,除非在编译调用者时设置了 DEBUG。

我个人根据情况使用两者:

Conditional("DEBUG") 示例:我使用它是为了在发布期间不必返回并编辑我的代码,但在调试期间我想确保我没有输入任何拼写错误。此函数检查我在尝试在 INotifyPropertyChanged 内容中使用它时是否正确键入了属性名称。

[Conditional("DEBUG")]
[DebuggerStepThrough]
protected void VerifyPropertyName(String propertyName)
{
    if (TypeDescriptor.GetProperties(this)[propertyName] == null)
        Debug.Fail(String.Format("Invalid property name. Type: {0}, Name: {1}",
            GetType(), propertyName));
}

您真的不想使用 #if DEBUG 创建函数,除非您愿意使用相同的 #if DEBUG 包装对该函数的每次调用:

#if DEBUG
    public void DoSomething() { }
#endif

    public void Foo()
    {
#if DEBUG
        DoSomething(); //This works, but looks FUGLY
#endif
    }

相对:

[Conditional("DEBUG")]
public void DoSomething() { }

public void Foo()
{
    DoSomething(); //Code compiles and is cleaner, DoSomething always
                   //exists, however this is only called during DEBUG.
}

#if DEBUG 示例:我在尝试为 WCF 通信设置不同的绑定时使用它。

#if DEBUG
        public const String ENDPOINT = "Localhost";
#else
        public const String ENDPOINT = "BasicHttpBinding";
#endif

在第一个示例中,所有代码都存在,但除非打开 DEBUG,否则将被忽略。在第二个示例中,const ENDPOINT 设置为“Localhost”或“BasicHttpBinding”,具体取决于是否设置了 DEBUG。

更新:我正在更新这个答案以澄清一个重要而棘手的问题。如果您选择使用 ConditionalAttribute,请记住在编译期间会省略调用,并且不是运行时。那是:

我的图书馆.dll

[Conditional("DEBUG")]
public void A()
{
    Console.WriteLine("A");
    B();
}

[Conditional("DEBUG")]
public void B()
{
    Console.WriteLine("B");
}

当库针对发布模式编译时(即没有 DEBUG 符号),它将永远省略从 A() 内对 B() 的调用,即使包含对 A() 的调用,因为 DEBUG 是在调用程序集中定义的.


DoSomething 的#if 调试不需要将所有调用语句都包含在#if DEBUG 中。您可以 1:仅在 DoSomething 内部进行#if 调试,或者使用 DoSomething 的空白定义执行 #else。您的评论仍然帮助我理解了差异,但#if DEBUG 不必像您展示的那样丑陋。
如果您只是#if DEBUG 内容,当您的代码在非调试版本中运行时,JIT 可能仍会包含对该函数的调用。使用 Conditional 属性意味着 JIT 知道在非调试构建中甚至不输出调用站点。
@JeffYates:我看不出你写的和我解释的有什么不同。
@Apeiron如果您在#if调试中只有函数内容,那么函数调用仍会添加到调用堆栈中,虽然这通常不是很重要,将声明和函数调用添加到#if意味着编译器的行为如下如果函数不存在,所以我的方法是使用#if 的更“正确”的方式。尽管这两种方法产生的结果在正常使用中彼此无法区分
如果有人想知道,IL = 中间语言 - en.wikipedia.org/wiki/Common_Intermediate_Language
J
Jon Skeet

好吧,值得注意的是,它们根本不是同一个意思。

如果未定义 DEBUG 符号,则在第一种情况下,不会调用 SetPrivateValue 本身...而在第二种情况下,它将存在,但任何编译时都没有DEBUG 符号将省略这些调用。

如果代码及其所有调用者都在同一个程序集中,则此差异不太很重要-但这意味着在第一种情况下,您需要在调用代码也是如此。

就我个人而言,我推荐第二种方法 - 但您确实需要清楚地记住它们之间的区别。


调用代码的 +1 也需要有 #if 语句。这意味着#if语句将会激增......
虽然第二个选项(条件属性)在某些情况下更好更简洁,但可能需要传达这样一个事实,即在编译期间(例如通过命名约定)将从程序集中剥离方法调用。
P
Paul Fleming

我敢肯定很多人会不同意我的观点,但是作为一个构建人员,我经常听到“但它可以在我的机器上运行!”,我认为你几乎不应该使用任何一种。如果你真的需要一些东西来进行测试和调试,想办法让可测试性与实际的生产代码分开。

在单元测试中使用模拟来抽象场景,为您想要测试的一次性场景制作一个版本的东西,但不要将用于调试的测试放入您为生产发布测试和编写的二进制代码的代码中。这些调试测试只是对开发人员隐藏了可能存在的错误,因此直到过程后期才被发现。


我完全同意你的看法,吉米。如果您在测试中使用 DI 和模拟,为什么您的代码中需要 #if debug 或任何类似的构造?
我们经常做一些事情,例如为自己设置默认收件人电子邮件,在调试版本中,使用 #if DEBUG,而不只是为了测试,这样我们在测试必须传输电子邮件作为过程的一部分的系统时不会意外地向其他人发送垃圾邮件。有时这些是工作的正确工具:)
我通常会同意你的观点,但如果你处于性能至高无上的情况,那么你不想用无关的日志记录和用户输出弄乱代码,但我 100% 同意它们不应该被用来改变基本行为
-1 使用其中任何一个都没有问题。声称单元测试和 DI 以某种方式取代了启用调试的产品构建是幼稚的。
我强烈反对。我有时会做并且一直发现有用的事情是在 #if DEBUG 中使用 throw 包装验证并在 #else 中不崩溃(可选地向我们发送异常电子邮件)来处理这种情况。例如,假设方法参数不应该为空,然后在#if DEBUG 中抛出如果它为空,但在#else 中分配一个默认值并向我们发送异常电子邮件。
s
sofsntp

这个也很有用:

if (Debugger.IsAttached)
{
...
}

就个人而言,与其他 2 个替代方案相比,我看不出这有什么用处。这保证了整个块都被编译,并且即使在发布版本中也必须在运行时调用 Debugger.IsAttached
P
P Daddy

对于第一个示例,如果未定义 DEBUG,则 SetPrivateValue 将不存在于构建中;对于第二个示例,如果 DEBUG } 没有定义。

对于第一个示例,您还必须使用 #if DEBUG 包装对 SetPrivateValue 的所有调用。

对于第二个示例,将省略对 SetPrivateValue 的调用,但请注意 SetPrivateValue 本身仍将被编译。如果您正在构建库,这很有用,因此引用您的库的应用程序仍然可以使用您的函数(如果满足条件)。

如果你想省略调用并节省被调用者的空间,你可以使用两种技术的组合:

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
    #if DEBUG
    // method body here
    #endif
}

M
Matt Smith

假设您的代码还有一个 #else 语句,它定义了一个空存根函数,解决了 Jon Skeet 的观点之一。两者之间还有第二个重要区别。

假设 #if DEBUGConditional 函数存在于您的主项目可执行文件所引用的 DLL 中。使用 #if,将针对库的编译设置执行条件评估。使用 Conditional 属性,将根据调用程序的编译设置执行条件评估。


Y
Yousha Aleayoub

我有一个 SOAP WebService 扩展来使用自定义 [TraceExtension] 记录网络流量。我仅将它用于 Debug 构建并从 Release 构建中省略。使用 #if DEBUG 包装 [TraceExtension] 属性,从而将其从 Release 构建中删除。

#if DEBUG
[TraceExtension]
#endif
[System.Web.Service.Protocols.SoapDocumentMethodAttribute( ... )]
[ more attributes ...]
public DatabaseResponse[] GetDatabaseResponse( ...) 
{
    object[] results = this.Invoke("GetDatabaseResponse",new object[] {
          ... parmeters}};
}

#if DEBUG
[TraceExtension]
#endif
public System.IAsyncResult BeginGetDatabaseResponse(...)

#if DEBUG
[TraceExtension]
#endif
public DatabaseResponse[] EndGetDatabaseResponse(...)

Y
Yashwant Shukla

通常你会在 Program.cs 中需要它,你想在其中决定在非调试代码上运行调试,而且主要在 Windows 服务中运行。所以我创建了一个只读字段 IsDebugMode 并在静态构造函数中设置它的值,如下所示。

static class Program
{

    #region Private variable
    static readonly bool IsDebugMode = false;
    #endregion Private variable

    #region Constrcutors
    static Program()
    {
 #if DEBUG
        IsDebugMode = true;
 #endif
    }
    #endregion

    #region Main

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main(string[] args)
    {

        if (IsDebugMode)
        {
            MyService myService = new MyService(args);
            myService.OnDebug();             
        }
        else
        {
            ServiceBase[] services = new ServiceBase[] { new MyService (args) };
            services.Run(args);
        }
    }

    #endregion Main        
}