2008-09-21 37 views
7

Tôi có đoạn mã sau:Ngăn chặn NET từ "nâng" biến địa phương

string prefix = "OLD:"; 
Func<string, string> prependAction = (x => prefix + x); 
prefix = "NEW:"; 
Console.WriteLine(prependAction("brownie")); 

Bởi vì trình biên dịch thay thế biến tiền tố với một kết thúc "MỚI: Brownie" được in ra cửa sổ Console.

Có cách nào dễ dàng để ngăn trình biên dịch nâng biến tiền tố trong khi vẫn sử dụng biểu thức lambda? Tôi muốn một cách làm cho công việc Func của tôi giống hệt:

Func<string, string> prependAction = (x => "OLD:" + x); 

Lý do tôi cần điều này là tôi muốn sắp xếp hàng loạt đại biểu kết quả. Nếu biến tiền tố nằm trong một lớp không tuần tự hóa thì hàm trên sẽ không tuần tự hóa.

Cách duy nhất xung quanh này, tôi có thể nhìn thấy vào lúc này là tạo ra một lớp serializable mới mà các cửa hàng chuỗi như là một biến thành viên và có phương pháp chuỗi thêm vào trước:

string prefix = "NEW:"; 
var prepender = new Prepender {Prefix = prefix}; 
Func<string, string> prependAction = prepender.Prepend; 
prefix = "OLD:"; 
Console.WriteLine(prependAction("brownie")); 

Với lớp helper:

[Serializable] 
public class Prepender 
{ 
    public string Prefix { get; set; } 
    public string Prepend(string str) 
    { 
     return Prefix + str; 
    } 
} 

Điều này có vẻ như rất nhiều công việc bổ sung để làm cho trình biên dịch bị "câm".

+0

Vì vậy, câu hỏi của bạn không phải là về đánh giá lười biếng, nhưng về việc tuần tự hóa? – Sam

Trả lời

8

Tôi thấy sự cố cơ bản ngay bây giờ. Nó sâu hơn tôi nghĩ. Về cơ bản giải pháp là sửa đổi cây biểu thức trước khi tuần tự hóa nó, bằng cách thay thế tất cả các subtrees không phụ thuộc vào các tham số với các nút liên tục. Điều này rõ ràng được gọi là "funcletization". Có giải thích về nó here.

+0

Liên kết msdn bị hỏng; Tôi nghĩ rằng liên kết chính xác hiện là http://social.msdn.microsoft.com/Forums/en-US/67f63b9a-ea44-4428-aea0-5dcdb61e918b/binding-lambdas-when-they-are-closures Thật không may, mã ở đó sử dụng FuncletExpression, một lớp mà tôi tin rằng đã bị loại bỏ khỏi khung lưới. – HugoRune

+0

@HugoRune: Cảm ơn bạn đã cập nhật liên kết. Tôi sẽ tưởng tượng rằng mã hoạt động, nếu bạn chỉ cần loại bỏ các trường hợp cho FuncletExpression (và thêm trường hợp cho bất kỳ ExpressionTypes đã được thêm từ). –

+0

Hoặc tìm cách này: http://dotnetinside.com/framework/v4.0.30319/System.Data.Linq/Funcletizer –

-1

gì về điều này

string prefix = "OLD:"; 
string _prefix=prefix; 
Func<string, string> prependAction = (x => _prefix + x); 
prefix = "NEW:"; 
Console.WriteLine(prependAction("brownie")); 
+0

Điều này không giải quyết được vấn đề serialization vì biến _prefix vẫn được gắn vào lớp chứa mã. – Brownie

-1

Làm thế nào về:

string prefix = "OLD:"; 
string prefixCopy = prefix; 
Func<string, string> prependAction = (x => prefixCopy + x); 
prefix = "NEW:"; 
Console.WriteLine(prependAction("brownie")); 

?

+0

Điều này không giải quyết được vấn đề serialization vì biến prefixCopy vẫn được gắn vào lớp chứa mã. – Brownie

1

Lambdas tự động 'hút' trong các biến cục bộ, tôi e rằng đó đơn giản là cách chúng hoạt động theo định nghĩa.

0

Đây là vấn đề khá phổ biến tức là biến được sửa đổi bởi một kết thúc vô tình - một giải pháp đơn giản hơn nhiều là chỉ để đi:

string prefix = "OLD:"; 
var actionPrefix = prefix; 
Func<string, string> prependAction = (x => actionPrefix + x); 
prefix = "NEW:"; 
Console.WriteLine(prependAction("brownie")); 

Nếu bạn đang sử dụng resharper nó sẽ thực sự xác định những nơi trong mã của bạn nơi bạn có nguy cơ gây ra các tác dụng phụ không mong muốn như thế này - vì vậy nếu tập tin là "tất cả màu xanh", mã của bạn phải được OK.

Tôi nghĩ rằng trong một số cách nó sẽ được tốt đẹp nếu chúng ta có một số đường cú pháp để xử lý tình trạng này vì vậy chúng tôi có thể viết nó như một lớp lót tức là

Func<string, string> prependAction = (x => ~prefix + x); 

Trường hợp một số nhà điều hành tiền tố sẽ gây ra giá trị của biến được đánh giá trước khi xây dựng hàm đại biểu/hàm ẩn danh.

+0

Tôi nghĩ bạn đã bỏ lỡ điểm của câu hỏi. Người được ủy nhiệm của bạn sẽ không thể tuần tự hóa được vì việc đóng cửa vẫn được hình thành, chỉ trên một biến khác. –

0

Đã có một số câu trả lời ở đây giải thích cách bạn có thể tránh biến lambda "nâng" biến của bạn. Thật không may là không giải quyết vấn đề cơ bản của bạn. Không thể serialize lambda không có gì để làm với lambda có "dỡ bỏ" biến của bạn. Nếu biểu thức lambda cần một thể hiện của một lớp không tuần tự hóa để tính toán, nó làm cho cảm giác hoàn hảo rằng nó không thể được tuần tự hóa.

Tùy thuộc vào những gì bạn thực sự đang cố gắng làm (tôi hoàn toàn không thể quyết định từ bài đăng của bạn), giải pháp sẽ là di chuyển phần không tuần tự hóa của lambda ra ngoài.

Ví dụ, thay vì:

NonSerializable nonSerializable = new NonSerializable(); 
Func<string, string> prependAction = (x => nonSerializable.ToString() + x); 

sử dụng:

NonSerializable nonSerializable = new NonSerializable(); 
string prefix = nonSerializable.ToString(); 
Func<string, string> prependAction = (x => prefix + x); 
+0

Nếu mã đó được chứa trong một phương thức không serializable lớp (Trong trường hợp của tôi một codebehind ASP.NET) thì prependAction sẽ vẫn tham chiếu đến lớp đó và không serialize. – Brownie

-1

Vâng, nếu chúng ta sẽ nói về "vấn đề" ở đây, lambdas đến từ thế giới lập trình chức năng, và trong một ngôn ngữ lập trình thuần túy chức năng, không có bài tập và do đó, vấn đề của bạn sẽ không bao giờ phát sinh bởi vì giá trị của tiền tố không bao giờ có thể thay đổi. Tôi hiểu C# nghĩ rằng thật thú vị khi nhập ý tưởng từ các chương trình chức năng (vì FP tuyệt vời!) Nhưng rất khó để làm cho nó đẹp, vì C# là và sẽ luôn là ngôn ngữ lập trình bắt buộc.

0

Tôi gặp vấn đề bây giờ: lambda tham chiếu đến lớp chứa có thể không được tuần tự hóa. Sau đó, làm điều gì đó như sau:

public void static Func<string, string> MakePrependAction(String prefix){ 
    return (x => prefix + x); 
} 

(Lưu ý từ khóa tĩnh.) Sau đó, lambda không cần tham chiếu lớp chứa.

+0

Hầu như đã hoạt động! Thật không may trình biên dịch tạo ra một lớp kín riêng để giữ biến tiền tố. Trình biên dịch tạo ra lớp không phải là serializable. – Brownie

2

Chỉ cần chắc đóng cửa khác ...

Say, một cái gì đó như:

var prepend = "OLD:"; 

Func<string, Func<string, string>> makePrepender = x => y => (x + y); 
Func<string, string> oldPrepend = makePrepender(prepend); 

prepend = "NEW:"; 

Console.WriteLine(oldPrepend("Brownie")); 

havn't thử nghiệm nó chưa như tôi không có quyền truy cập vào VS vào lúc này, nhưng thông thường, điều này là cách tôi giải quyết vấn đề như vậy.

+0

Trong khi điều này làm đầu ra "OLD: Brownie" một đóng cửa vẫn được hình thành và do đó việc tuần tự hóa vẫn không thành công. –

Các vấn đề liên quan