C# #如果调试与条件(“调试”)

C# #如果调试与条件(“调试”),c#,debugging,preprocessor,debug-symbols,C#,Debugging,Preprocessor,Debug Symbols,在大型项目中使用哪个更好,以及为什么: #if DEBUG public void SetPrivateValue(int value) { ... } #endif 或 嗯,值得注意的是,它们的意思完全不同 如果未定义调试符号,则在第一种情况下,SetPrivateValue本身将不会被调用。。。而在第二种情况下,它将存在,但是任何编译时没有调试符号的调用程序都将忽略这些调用 如果代码及其所有调用者都在同一个程序集中,那么这种差异就不那么重要了——但这意味着在第一种情况下,您

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

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


嗯,值得注意的是,它们的意思完全不同

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

如果代码及其所有调用者都在同一个程序集中,那么这种差异就不那么重要了——但这意味着在第一种情况下,您还需要在调用代码周围设置
#If DEBUG


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

对于第一个示例,如果未定义
DEBUG
,则构建中将不存在
SetPrivateValue
,对于第二个示例,如果未定义
DEBUG
,则对
SetPrivateValue
调用将不存在于生成中

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

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

如果要省略调用并节省被调用方的空间,可以结合使用以下两种技术:

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

这取决于你的目标:

  • #如果调试
    :这里的代码在发布时甚至不会到达IL
  • [条件(“调试”)]
    :此代码将到达IL,但是对方法的调用将被忽略,除非在编译调用方时设置了调试
就我个人而言,我会根据情况使用这两种方法:

有条件(“调试”)示例:我使用它是为了不必在以后的发布期间返回并编辑代码,但在调试期间,我希望确保没有任何打字错误。当尝试在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.
}

#如果调试示例:我在尝试为WCF通信设置不同的绑定时使用此选项

#if DEBUG
        public const String ENDPOINT = "Localhost";
#else
        public const String ENDPOINT = "BasicHttpBinding";
#endif
在第一个示例中,所有代码都存在,但只有在启用调试时才会被忽略。在第二个示例中,const端点设置为“Localhost”或“BasicHttpBinding”,具体取决于是否设置了DEBUG


更新:我更新这个答案是为了澄清一个重要而棘手的问题。如果选择使用
条件属性
,请记住编译期间会忽略调用,而不是运行时。即:

MyLibrary.dll

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

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

当库按照发布模式编译时(即没有调试符号),它将永远忽略从
A()
中调用
B()
,即使调用
A()之所以包含
,是因为调试是在调用程序集中定义的。

让我们假设您的代码也有一个#else语句,该语句定义了一个空存根函数,寻址Jon Skeet的一个点。这两者之间还有第二个重要区别


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

我相信很多人都会不同意我的观点,但作为构建人员,他们经常听到“但它在我的机器上工作!”,我的观点是,你几乎不应该使用这两种方法。如果您确实需要测试和调试,请找出一种方法使可测试性与实际生产代码分离


抽象单元测试中的模拟场景,为您想要测试的一次性场景制作一次性版本,但不要将调试测试放入为生产版本测试和编写的二进制代码中。这些调试测试只是对开发人员隐藏了可能的bug,所以直到过程的后期才会发现它们

我有一个SOAP-WebService扩展,可以使用自定义的
[TraceExtension]
记录网络流量。我只在调试构建时使用它,而在发布构建时忽略它。使用
#if DEBUG
包装
[TraceExtension]
属性,从而将其从版本中删除

#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(...)

这一点也很有用:

if (Debugger.IsAttached)
{
...
}

通常,您需要在Program.cs中使用它,在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        
}
静态类程序
{
#区域私有变量
静态只读bool IsDebugMode=false;
#端域私有变量
#区域构造
静态程序()
{
#如果调试
IsDebugMode=true;
#恩迪夫
}
#端区
#主要地区
/// 
///应用程序的主要入口点。
/// 
静态void Main(字符串[]参数)
{
如果(IsDebugMode)
{
MyService MyService=新的MyService(args);
myService.OnDebug();
}
其他的
{
ServiceBase[]services=newservicebase[]{newmyservice(args)};
服务运行(args);
}
}
#端区干线
}
+1用于
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        
}