Gọi return bên trong bằng câu lệnh {} có phải là một cách tốt không?


93

Tôi chỉ muốn biết cách gọi returnbên trong một usingkhối có an toàn / tốt hay không.

Đối với người yêu cũ.

using(var scope = new TransactionScope())
{
  // my core logic
  return true; // if condition met else
  return false;
  scope.Complete();
}

Chúng ta biết dấu ngoặc nhọn cuối cùng dispose()sẽ bị tắt. Nhưng điều gì sẽ xảy ra trong trường hợp trên, vì returnnhảy điều khiển ra khỏi phạm vi đã cho (AFAIK) ...

  1. Tôi scope.Complete()có được gọi không?
  2. Và đối với dispose()method của scope cũng vậy .

1
Khi using{}phạm vi kết thúc, các đối tượng liên quan được xử lý, returnsẽ "phá vỡ" phạm vi - vì vậy các đối tượng sẽ bị xử lý như mong đợi
Shai

4
Hãy lưu ý rằng scope.Complete()cuộc gọi của bạn sẽ không bao giờ được thực hiện với mẫu bạn đã cung cấp, vì vậy giao dịch của bạn sẽ luôn quay trở lại.
Andy

Bất kể using's dispose()có được gọi hay không, khi bạn trả về, hàm chứa usingkhối này sẽ được trả về và mọi thứ thuộc về nó sẽ không còn nữa. Vì vậy, ngay cả khi scopekhông được xử lý bởi "bởi using" (nó sẽ như vậy, như những người khác giải thích), nó vẫn sẽ bị loại bỏ bởi vì chức năng đã kết thúc. Nếu C # có gototuyên bố - bạn đã cười xong chưa? tốt- sau đó thay vì quay trở lại, bạn có thể quay lại gotosau dấu ngoặc nhọn đóng, mà không cần quay lại. Về mặt logic, scopevẫn sẽ được xử lý, nhưng bạn chỉ cần đưa gotovào C # nên ai quan tâm đến logic ở giai đoạn đó.
Superbest


Câu trả lời:


146

Hoàn toàn an toàn khi gọi returnbên trong usingkhối của bạn , vì khối đang sử dụng chỉ là một try/finallykhối.

Trong ví dụ của bạn ở trên sau khi trả về true, phạm vi sẽ bị loại bỏ và giá trị được trả lại. return false, và scope.Complete()sẽ không được gọi. Disposetuy nhiên sẽ được gọi bất kể nó nằm bên trong khối cuối cùng.

Mã của bạn về cơ bản giống như sau (nếu điều đó làm cho nó dễ hiểu hơn):

var scope = new TransactionScope())
try
{
  // my core logic
  return true; // if condition met else
  return false;
  scope.Complete();
}
finally
{
  if( scope != null) 
    ((IDisposable)scope).Dispose();
}

Xin lưu ý rằng giao dịch của bạn sẽ không bao giờ được cam kết vì không có cách nào scope.Complete()để thực hiện giao dịch.


13
Bạn nên nói rõ rằng Dispose sẽ được gọi. Nếu OP không biết chuyện gì xảy ra using, rất có thể anh ta không biết chuyện gì xảy ra finally.
Konrad Rudolph

Bạn có thể để lại một khối using với return, nhưng trong trường hợp của TransactionScope, bạn có thể gặp rắc rối với chính câu lệnh using: blogs.msdn.com/b/florinlazar/archive/2008/05/05/8459994.aspx
thewhiteambit

Theo kinh nghiệm của tôi, điều này không hoạt động với SQL Server CLR Assemblies. Khi tôi cần trả về kết quả cho một UDF chứa trường SqlXml đang tham chiếu đến một Đối tượng Dòng nhớ. Tôi nhận được thông báo " Không thể truy cập một đối tượng đã xử lý " và " Cố gắng gọi Read không hợp lệ khi luồng bị đóng. ", Vì vậy, tôi ĐÃ CẦN viết mã rò rỉ và bỏ qua câu lệnh using trong trường hợp này. :( hy vọng duy nhất của tôi là CLR SQL sẽ xử lý xử lý các đối tượng này Đó là một kịch bản duy nhất, nhưng nghĩ rằng tôi muốn chia sẻ..
MikeTeeVee

1
@MikeTeeVee - giải pháp Cleaner là một trong hai (a) có người gọi làm việc using, ví dụ using (var callersVar = MyFunc(..)) .., thay vì của việc có bằng cách sử dụng bên trong "MyFunc" - Ý tôi là người gọi được đưa ra con suối và có trách nhiệm đóng nó qua usinghay rõ ràng, hoặc (b) yêu cầu MyFunc trích xuất bất kỳ thông tin nào cần thiết vào các đối tượng khác, thông tin đó có thể được chuyển trở lại một cách an toàn - sau đó các đối tượng hoặc luồng dữ liệu bên dưới có thể được bạn xử lý using. Bạn không cần phải viết mã bị rò rỉ.
ToolmakerSteve

6

Điều đó tốt - finallycác mệnh đề (là những gì dấu ngoặc nhọn đóng của usingmệnh đề làm dưới mui xe) luôn được thực thi khi phạm vi được để lại, bất kể như thế nào.

Tuy nhiên, điều này chỉ đúng với các câu lệnh nằm trong khối cuối cùng (không thể đặt rõ ràng khi sử dụng using). Do đó, trong ví dụ của bạn, scope.Complete()sẽ không bao giờ được gọi (tôi hy vọng trình biên dịch sẽ cảnh báo bạn về mã không thể truy cập được).


2

Nói chung, đó là một cách tiếp cận tốt. Nhưng trong trường hợp của bạn, nếu bạn quay lại trước khi gọi scope.Complete(), nó sẽ chỉ làm thùng rác TransactionScope. Phụ thuộc vào thiết kế của bạn.

Vì vậy, trong mẫu này, Complete () không được gọi và phạm vi được xử lý, giả sử nó đang kế thừa giao diện IDisposable.


Nó phải triển khai IDisposable hoặc sử dụng wont compile.
Chriseyre2000

2

scope.Complete chắc chắn nên được gọi trước return. Trình biên dịch sẽ hiển thị cảnh báo và mã này sẽ không bao giờ được gọi.

Về returnbản thân - vâng, có thể an toàn khi gọi nó là usingcâu lệnh bên trong . Việc sử dụng được dịch sang khối try-final phía sau cảnh và cuối cùng khối chắc chắn được thực thi.


1

Trong ví dụ bạn đã cung cấp, có một vấn đề; scope.Complete()không bao giờ được gọi. Thứ hai, nó không phải là một thực hành tốt để sử dụng returncâu lệnh bên trong usingcâu lệnh. Tham khảo nội dung sau:

using(var scope = new TransactionScope())
{
    //have some logic here
    return scope;      
}

Trong ví dụ đơn giản này, vấn đề là; giá trị của scopesẽ là null khi kết thúc sử dụng câu lệnh.

Vì vậy, tốt hơn là không quay lại bên trong bằng cách sử dụng các câu lệnh.


1
Chỉ vì 'phạm vi trả về' là vô nghĩa, nó không có nghĩa là câu lệnh trả về là sai.
Tăng đoàn

chỉ vì việc không sử dụng các phương pháp hay nhất không có nghĩa là bạn đã làm sai. Nó ngụ ý, tốt nhất là nên tránh, vì nó có thể dẫn đến những hậu quả không lường trước được.
daryal

1
Giá trị của scopewill không bị null - điều duy nhất sẽ xảy ra là giá trị Dispose()sẽ được gọi trên phiên bản đó, và do đó phiên bản đó sẽ không được sử dụng nữa (nhưng nó không phải là null và không có gì ngăn cản bạn thử và sử dụng đối tượng được xử lý, mặc dù đây thực sự là cách sử dụng không phù hợp đối với đối tượng dùng một lần).
Lucero

Lucero khá chính xác. Các đối tượng dùng một lần không bị rỗng sau khi được xử lý. Thuộc tính IsDisposed của nó là true, nhưng nếu bạn kiểm tra với null, bạn sẽ nhận được false và return scopetrả về một tham chiếu đến đối tượng đó . Bằng cách này, nếu bạn gán tham chiếu đó ngược lại, bạn sẽ ngăn GC dọn dẹp đối tượng đã xử lý.
ThunderGr

1

Để đảm bảo rằng di scope.Complete()chúc được gọi, hãy bọc nó bằng try/finally. Các disposeđược gọi là bởi vì bạn phải quấn nó với usingđó là một sự thay thế try/finallykhối.

using(var scope = new TransactionScope())
{
  try
  {
  // my core logic
  return true; // if condition met else
  return false;
  }
  finally
  {
   scope.Complete();
  }
}

Tôi nghĩ bạn muốn nói Nếu bạn thắng - Nếu bạn muốn, không sẽ không ... theo mã của bạn. :)

0

Trong ví dụ này, scope.Complete () sẽ không bao giờ thực thi. Tuy nhiên, lệnh return sẽ dọn dẹp mọi thứ được gán trên ngăn xếp. GC sẽ xử lý mọi thứ không được tham chiếu. Vì vậy, trừ khi có một đối tượng mà GC không thể nhặt được, không có vấn đề gì.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.