Cái nào tốt hơn để sử dụng, và tại sao, trong một dự án lớn:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
hoặc là
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
Cái nào tốt hơn để sử dụng, và tại sao, trong một dự án lớn:
#if DEBUG
public void SetPrivateValue(int value)
{ ... }
#endif
hoặc là
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }
Câu trả lời:
Nó thực sự phụ thuộc vào những gì bạn sẽ làm:
#if DEBUG
: Mã ở đây thậm chí sẽ không đạt được IL khi phát hành.[Conditional("DEBUG")]
: Mã này sẽ đến IL, tuy nhiên các lệnh gọi đến phương thức sẽ bị bỏ qua trừ khi DEBUG được đặt khi người gọi được biên dịch.Cá nhân tôi sử dụng cả hai tùy thuộc vào tình huống:
Ví dụ có điều kiện ("DEBUG"): Tôi sử dụng điều này để tôi không phải quay lại và chỉnh sửa mã của mình sau khi phát hành, nhưng trong quá trình gỡ lỗi tôi muốn chắc chắn rằng mình đã không mắc lỗi chính tả nào. Hàm này kiểm tra xem tôi nhập tên thuộc tính một cách chính xác khi cố gắng sử dụng nó trong công cụ INotifyPropertyChanged của tôi.
[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));
}
Bạn thực sự không muốn tạo một hàm bằng cách sử dụng #if DEBUG
trừ khi bạn sẵn sàng kết thúc mọi cuộc gọi đến hàm đó bằng #if DEBUG
:
#if DEBUG
public void DoSomething() { }
#endif
public void Foo()
{
#if DEBUG
DoSomething(); //This works, but looks FUGLY
#endif
}
đấu với:
[Conditional("DEBUG")]
public void DoSomething() { }
public void Foo()
{
DoSomething(); //Code compiles and is cleaner, DoSomething always
//exists, however this is only called during DEBUG.
}
Ví dụ #if DEBUG: Tôi sử dụng điều này khi cố gắng thiết lập các ràng buộc khác nhau cho giao tiếp WCF.
#if DEBUG
public const String ENDPOINT = "Localhost";
#else
public const String ENDPOINT = "BasicHttpBinding";
#endif
Trong ví dụ đầu tiên, tất cả mã đều tồn tại, nhưng chỉ bị bỏ qua trừ khi bật DEBUG. Trong ví dụ thứ hai, const ENDPOINT được đặt thành "Localhost" hoặc "BasicHttpBinding" tùy thuộc vào việc DEBUG có được đặt hay không.
Cập nhật: Tôi đang cập nhật câu trả lời này để làm rõ một điểm quan trọng và khó khăn. Nếu bạn chọn sử dụng ConditionalAttribute
, hãy nhớ rằng các cuộc gọi bị bỏ qua trong quá trình biên dịch và không chạy . Đó là:
MyL Library.dll
[Conditional("DEBUG")]
public void A()
{
Console.WriteLine("A");
B();
}
[Conditional("DEBUG")]
public void B()
{
Console.WriteLine("B");
}
Khi thư viện được biên dịch theo chế độ phát hành (nghĩa là không có ký hiệu DEBUG), nó sẽ mãi mãi có cuộc gọi đến B()
từ bên trong A()
bị bỏ qua, ngay cả khi một cuộc gọi đến A()
được bao gồm vì DEBUG được xác định trong cụm gọi.
Chà, đáng chú ý là chúng không có nghĩa giống nhau chút nào.
Nếu biểu tượng DEBUG không được xác định, thì trong trường hợp đầu tiên, SetPrivateValue
chính nó sẽ không được gọi ... trong trường hợp thứ hai nó sẽ tồn tại, nhưng bất kỳ người gọi nào được biên dịch mà không có biểu tượng DEBUG sẽ bị bỏ qua các cuộc gọi đó.
Nếu mã và tất cả các trình gọi của nó nằm trong cùng một cụm thì sự khác biệt này ít quan trọng hơn - nhưng điều đó có nghĩa là trong trường hợp đầu tiên, bạn cũng cần phải có #if DEBUG
xung quanh mã gọi .
Cá nhân tôi muốn giới thiệu cách tiếp cận thứ hai - nhưng bạn cần phải giữ sự khác biệt giữa chúng rõ ràng trong đầu.
Tôi chắc chắn sẽ không đồng ý với tôi, nhưng đã dành thời gian làm nhân viên xây dựng liên tục nghe "Nhưng nó hoạt động trên máy của tôi!", Tôi đưa ra quan điểm rằng bạn cũng không bao giờ nên sử dụng. Nếu bạn thực sự cần một cái gì đó để thử nghiệm và gỡ lỗi, hãy tìm ra một cách để làm cho khả năng kiểm tra đó tách biệt khỏi mã sản xuất thực tế.
Tóm tắt các kịch bản với chế độ thử nghiệm đơn vị, tạo một phiên bản của mọi thứ cho một kịch bản bạn muốn kiểm tra, nhưng đừng đặt thử nghiệm để gỡ lỗi vào mã cho các nhị phân mà bạn kiểm tra và viết để phát hành sản xuất. Các thử nghiệm gỡ lỗi này chỉ ẩn các lỗi có thể có từ các nhà phát triển để chúng không được tìm thấy cho đến sau này trong quy trình.
#if debug
hoặc bất kỳ cấu trúc tương tự nào trong mã của bạn?
#if DEBUG
để chúng tôi không vô tình spam người khác trong khi kiểm tra một hệ thống phải truyền email như một phần của quy trình. Đôi khi đây là những công cụ phù hợp cho công việc :)
Điều này cũng có thể hữu ích:
if (Debugger.IsAttached)
{
...
}
Debugger.IsAttached
phải được gọi trong thời gian chạy ngay cả trong các bản dựng phát hành.
Với ví dụ đầu tiên, SetPrivateValue
sẽ không tồn tại trong xây dựng nếu DEBUG
không được định nghĩa, với ví dụ thứ hai, gọi đến SetPrivateValue
sẽ không tồn tại trong xây dựng nếuDEBUG
không được định nghĩa.
Với ví dụ đầu tiên, bạn sẽ phải thực hiện bất kỳ cuộc gọi nào SetPrivateValue
với#if DEBUG
là tốt.
Với ví dụ thứ hai, các cuộc gọi đến SetPrivateValue
sẽ bị bỏ qua, nhưng lưu ý rằngSetPrivateValue
chính nó vẫn sẽ được biên dịch. Điều này hữu ích nếu bạn đang xây dựng thư viện, vì vậy một ứng dụng tham chiếu thư viện của bạn vẫn có thể sử dụng chức năng của bạn (nếu điều kiện được đáp ứng).
Nếu bạn muốn bỏ qua các cuộc gọi và tiết kiệm không gian của callee, bạn có thể sử dụng kết hợp cả hai kỹ thuật:
[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
#if DEBUG
// method body here
#endif
}
#if DEBUG
xung quanh Conditional("DEBUG")
không loại bỏ các cuộc gọi đến chức năng đó, nó chỉ loại bỏ hoàn toàn chức năng khỏi IL, do đó bạn vẫn có các lệnh gọi đến chức năng không tồn tại (lỗi biên dịch).
Giả sử mã của bạn cũng có một #else
câu lệnh xác định hàm null, giải quyết một trong những điểm của Jon Skeet. Có một sự khác biệt quan trọng thứ hai giữa hai.
Giả sử #if DEBUG
hoặc Conditional
hàm tồn tại trong một DLL được tham chiếu bởi thực thi dự án chính của bạn. Sử dụng #if
, việc đánh giá điều kiện sẽ được thực hiện liên quan đến cài đặt biên dịch của thư viện. Sử dụng Conditional
thuộc tính, việc đánh giá điều kiện sẽ được thực hiện liên quan đến các cài đặt biên dịch của invoker.
Tôi có tiện ích mở rộng SOAP WebService để ghi lưu lượng truy cập mạng bằng cách sử dụng tùy chỉnh [TraceExtension]
. Tôi chỉ sử dụng điều này cho các bản dựng Debug và bỏ qua các bản dựng Phát hành . Sử dụng #if DEBUG
để bọc [TraceExtension]
thuộc tính do đó loại bỏ nó khỏi các bản dựng phát hành .
#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(...)
Thông thường, bạn sẽ cần nó trong Program.cs nơi bạn muốn quyết định chạy Debug trên mã Non-Debug và điều đó chủ yếu là trong Windows Services. Vì vậy, tôi đã tạo một trường chỉ đọc IsDebugMode và đặt giá trị của nó trong hàm tạo tĩnh như dưới đây.
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
}