在大型项目中哪个更好用,以及为什么:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
或者
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
这真的取决于你要做什么:
#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 是在调用程序集中定义的.
好吧,值得注意的是,它们根本不是同一个意思。
如果未定义 DEBUG 符号,则在第一种情况下,不会调用 SetPrivateValue
本身...而在第二种情况下,它将存在,但任何编译时都没有DEBUG 符号将省略这些调用。
如果代码及其所有调用者都在同一个程序集中,则此差异不太很重要-但这意味着在第一种情况下,您也需要在调用代码也是如此。
就我个人而言,我推荐第二种方法 - 但您确实需要清楚地记住它们之间的区别。
我敢肯定很多人会不同意我的观点,但是作为一个构建人员,我经常听到“但它可以在我的机器上运行!”,我认为你几乎不应该使用任何一种。如果你真的需要一些东西来进行测试和调试,想办法让可测试性与实际的生产代码分开。
在单元测试中使用模拟来抽象场景,为您想要测试的一次性场景制作一个版本的东西,但不要将用于调试的测试放入您为生产发布测试和编写的二进制代码的代码中。这些调试测试只是对开发人员隐藏了可能存在的错误,因此直到过程后期才被发现。
#if debug
或任何类似的构造?
#if DEBUG
,而不只是为了测试,这样我们在测试必须传输电子邮件作为过程的一部分的系统时不会意外地向其他人发送垃圾邮件。有时这些是工作的正确工具:)
这个也很有用:
if (Debugger.IsAttached)
{
...
}
Debugger.IsAttached
。
对于第一个示例,如果未定义 DEBUG
,则 SetPrivateValue
将不存在于构建中;对于第二个示例,如果 DEBUG
} 没有定义。
对于第一个示例,您还必须使用 #if DEBUG
包装对 SetPrivateValue
的所有调用。
对于第二个示例,将省略对 SetPrivateValue
的调用,但请注意 SetPrivateValue
本身仍将被编译。如果您正在构建库,这很有用,因此引用您的库的应用程序仍然可以使用您的函数(如果满足条件)。
如果你想省略调用并节省被调用者的空间,你可以使用两种技术的组合:
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
#if DEBUG
// method body here
#endif
}
假设您的代码还有一个 #else
语句,它定义了一个空存根函数,解决了 Jon Skeet 的观点之一。两者之间还有第二个重要区别。
假设 #if DEBUG
或 Conditional
函数存在于您的主项目可执行文件所引用的 DLL 中。使用 #if
,将针对库的编译设置执行条件评估。使用 Conditional
属性,将根据调用程序的编译设置执行条件评估。
我有一个 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(...)
通常你会在 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
}