在大型项目中,哪种方法更好,为什么使用:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
要么
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
我有一个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(...)
好吧,值得注意的是,它们根本不是同一件事。
如果未定义DEBUG符号,则在第一种情况下,不会调用SetPrivateValue
本身...而在第二种情况下,它将存在,但是编译时没有DEBUG符号的任何调用程序都将忽略这些调用。
如果代码及其所有调用者都在同一程序集中,则此区别并不重要-但这意味着在第一种情况下,您还需要在调用代码周围还包含#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
}
这实际上取决于您要做什么:
#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,否则它将被忽略。 在第二个示例中,取决于是否设置了DEBUG,将const ENDPOINT设置为“ Localhost”或“ BasicHttpBinding”。
更新:我正在更新此答案以阐明重要且棘手的问题。 如果选择使用ConditionalAttribute
,请记住,在编译过程中会忽略调用,而在运行时则不会 。 那是:
MyLibrary.dll
[Conditional("DEBUG")]
public void A()
{
Console.WriteLine("A");
B();
}
[Conditional("DEBUG")]
public void B()
{
Console.WriteLine("B");
}
当针对发布模式(即没有DEBUG符号)编译该库时,即使包括了对A()
的调用,也会永远忽略从A()
对B()
的调用,因为在调用程序集中定义了DEBUG 。
假设您的代码还具有#else
语句,该语句定义了一个空存根函数,用于解决Jon Skeet的要点之一。 两者之间还有第二个重要区别。
假设#if DEBUG
或Conditional
函数存在于主项目可执行文件引用的DLL中。 使用#if
,将对库的编译设置执行条件的评估。 使用Conditional
属性,将针对调用程序的编译设置执行条件的评估。
我敢肯定,很多人会不同意我的观点,但是作为一名构建专家,我经常听到“但是它在我的机器上可以工作!”,我认为您几乎永远都不要使用。 如果您确实需要测试和调试的东西,请找出一种使可测试性与实际生产代码分开的方法。
在单元测试中使用模拟对场景进行抽象,为要测试的一种场景创建一种版本的事物,但不要将要调试的测试放入要为生产发行版测试和编写的二进制代码中。 这些调试测试只是隐藏了开发人员可能遇到的错误,因此直到该过程的后期才发现它们。
这也可能有用:
if (Debugger.IsAttached)
{
...
}
通常,您需要在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
}