在大型项目中使用哪个更好,为什么更好:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
要么
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
这真的取决于你的目标:
#if DEBUG
:这里的代码甚至不会在发布时到达IL。[Conditional("DEBUG")]
:此代码将到达IL,但是除非在编译调用者时设置了DEBUG,否则将省略对该方法的调用。我根据具体情况个人使用:
条件(“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
,请记住在编译期间省略了调用,而不是运行时。那是:
MyLibrary.dll
[Conditional("DEBUG")]
public void A()
{
Console.WriteLine("A");
B();
}
[Conditional("DEBUG")]
public void B()
{
Console.WriteLine("B");
}
当库是针对发布模式编译的(即没有DEBUG符号)时,它将永远从B()
中省略对A()
的调用,即使包含对A()
的调用,因为在调用程序集中定义了DEBUG。
嗯,值得注意的是,它们并不意味着完全相同。
如果未定义DEBUG符号,则在第一种情况下不会调用SetPrivateValue
本身...而在第二种情况下它将存在,但是没有DEBUG符号编译的任何调用者将省略这些调用。
如果代码及其所有调用者都在同一个程序集中,那么这种差异就不那么重要了 - 但这意味着在第一种情况下你还需要在调用代码周围使用#if DEBUG
。
就个人而言,我建议采用第二种方法 - 但你确实需要保持它们之间的区别。
我肯定会有很多人不同意我的看法,但是花了很多时间作为一个不断听到的“但它能在我的机器上运行!”,我认为你应该从不使用它们。如果您确实需要一些测试和调试,请找出一种方法,使该可测试性与实际生产代码分开。
在单元测试中使用模拟进行抽象的场景,为要测试的一个场景制作一个关闭版本的东西,但是不要将调试测试放入为测试和编写用于生产版本的二进制文件的代码中。这些调试测试只是隐藏了开发人员可能出现的错误,因此在此过程的后期才能找到它们。
这个也很有用:
if (Debugger.IsAttached)
{
...
}
对于第一个示例,如果未定义SetPrivateValue
,则DEBUG
将不存在于构建中。对于第二个示例,如果未定义SetPrivateValue
,则构建中将不存在对DEBUG
的调用。
在第一个例子中,你必须用SetPrivateValue
包装对#if DEBUG
的任何调用。
在第二个例子中,将省略对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中使用它,在那里您要决定在非调试代码上运行Debug,而在Windows Services中运行太多。所以我创建了一个只读字段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
}