문제

다음 코드가 있습니다.

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

컴파일러는 접두사 변수를 클로저로 대체하기 때문에 "NEW:brownie"가 콘솔에 인쇄됩니다.

람다 식을 사용하면서 컴파일러가 접두사 변수를 해제하는 것을 방지하는 쉬운 방법이 있습니까?내 Func를 다음과 동일하게 작동시키는 방법을 원합니다.

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

이것이 필요한 이유는 결과 대리자를 직렬화하고 싶기 때문입니다.접두사 변수가 직렬화할 수 없는 클래스에 있으면 위 함수는 직렬화되지 않습니다.

현재 이 문제를 해결할 수 있는 유일한 방법은 문자열을 멤버 변수로 저장하고 문자열 앞에 추가하는 메서드가 있는 새로운 직렬화 가능 클래스를 만드는 것입니다.

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

도우미 클래스 사용:

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

컴파일러를 "멍청하게" 만드는 데는 추가 작업이 많이 필요한 것처럼 보입니다.

도움이 되었습니까?

해결책

이제 근본적인 문제가 보입니다.처음 생각했던 것보다 더 깊네요.기본적으로 해결책은 매개변수에 의존하지 않는 모든 하위 트리를 상수 노드로 대체하여 표현식 트리를 직렬화하기 전에 수정하는 것입니다.이것은 분명히 "기능화"라고 불립니다.그에 대한 설명이 있습니다 여기.

다른 팁

그냥 또 닫으세요...

다음과 같이 말해보세요.

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"));

현재 VS에 액세스할 수 없기 때문에 아직 테스트하지는 않았지만 일반적으로 이러한 문제를 해결하는 방법은 다음과 같습니다.

람다는 지역 변수를 자동으로 '흡수'합니다. 유감스럽게도 그것이 정의에 따라 작동하는 방식일 뿐입니다.

이것은 매우 일반적인 문제입니다.의도치 않게 클로저에 의해 변수가 수정되는 경우 - 훨씬 간단한 해결책은 다음과 같습니다.

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

resharper를 사용하는 경우 실제로 코드에서 이와 같은 예상치 못한 부작용이 발생할 위험이 있는 위치를 식별합니다. 따라서 파일이 "모두 녹색"이면 코드는 정상입니다.

어떤 면에서는 이 상황을 처리할 수 있는 구문 설탕이 있어서 한 줄로 작성할 수 있었으면 좋았을 것이라고 생각합니다.

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

일부 접두사 연산자로 인해 익명 대리자/함수를 구성하기 전에 변수 값이 평가됩니다.

이제 문제가 발생합니다.람다는 직렬화할 수 없는 포함 클래스를 나타냅니다.그런 다음 다음과 같이 하십시오.

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

(정적 키워드에 유의하세요.) 그러면 람다는 포함 클래스를 참조할 필요가 없습니다.

여기에는 람다가 변수를 "리프팅"하는 것을 방지하는 방법을 설명하는 몇 가지 답변이 이미 있습니다.불행히도 그것은 근본적인 문제를 해결하지 못합니다.람다를 직렬화할 수 없다는 것은 람다가 변수를 "리프트"한 것과는 아무런 관련이 없습니다.람다 식에 계산을 위해 직렬화되지 않은 클래스의 인스턴스가 필요한 경우 직렬화할 수 없다는 것이 완벽하게 이해됩니다.

실제로 수행하려는 작업에 따라(귀하의 게시물에서 결정할 수 없음) 람다의 직렬화할 수 없는 부분을 외부로 옮기는 것이 해결책입니다.

예를 들면 다음과 같습니다.

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

사용:

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

이건 어때?

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

어떻습니까:

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

?

글쎄, 여기서 "문제"에 대해 이야기한다면 람다는 함수형 프로그래밍 세계에서 왔으며 순전히 함수형 프로그래밍 언어에서 왔습니다. 과제가 없어 접두사의 값은 절대로 변경될 수 없기 때문에 문제가 발생하지 않습니다.나는 C#이 기능적 프로그램에서 아이디어를 가져오는 것이 멋지다고 생각한다는 것을 이해합니다. ~이다 멋지네요!) 하지만 C#은 명령형 프로그래밍 언어이고 앞으로도 그럴 것이기 때문에 예쁘게 만드는 것은 매우 어렵습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top