c# - 변수 - razor 페이지




표현식/문장 트리 (2)

try / finally에서 IEnumerator를 삭제하는 것을 잊지 마십시오. - 많은 코드 (예 : File.ReadLines ())가 이에 따라 달라집니다.

업데이트 된 질문 추가 사항

.NET에서 표현식 트리를 사용하여 런타임시 코드를 생성 해본 결과 표현 트리를 작성하여 foreach 문을 구현하려고했습니다.

결국 표현은 다음을 수행하는 대리자를 생성 할 수 있어야합니다.

Action<IEnumerable<int>> action = source => 
{
  var enumerator = source.GetEnumerator();
  while(enumerator.MoveNext())
  {
    var i = enumerator.Current;
    // the body of the foreach that I don't currently have yet
  }
}

IEnumerable에서 BlockExpression을 생성하는 다음 도우미 메서드를 생각해 냈습니다.

public static BlockExpression ForEachExpr<T>(this IEnumerable<T> source, string collectionName, string itemName)
{
        var item = Expression.Variable(typeof(T), itemName);

        var enumerator = Expression.Variable(typeof(IEnumerator<T>), "enumerator");

        var param = Expression.Parameter(typeof(IEnumerable<T>), collectionName);

        var doMoveNext = Expression.Call(enumerator, typeof(IEnumerator).GetMethod("MoveNext"));

        var assignToEnum = Expression.Assign(enumerator, Expression.Call(param, typeof(IEnumerable<T>).GetMethod("GetEnumerator")));

        var assignCurrent = Expression.Assign(item, Expression.Property(enumerator, "Current"));

        var @break = Expression.Label();

        var @foreach = Expression.Block(
            assignToEnum,
            Expression.Loop(
                Expression.IfThenElse(
                Expression.NotEqual(doMoveNext, Expression.Constant(false)),
                    assignCurrent
                , Expression.Break(@break))
            ,@break)
        );
        return @foreach;

}

다음 코드는 :

var ints = new List<int> { 1, 2, 3, 4 };
var expr = ints.ForEachExpr("ints", "i");
var lambda = Expression.Lambda<Action<IEnumerable<int>>>(expr, Expression.Parameter(typeof(IEnumerable<int>), "ints"));

이 표현식 트리를 생성합니다 :

.Lambda #Lambda1<System.Action`1[System.Collections.Generic.IEnumerable`1[System.Int32]]>(System.Collections.Generic.IEnumerable`1[System.Int32] $ints)
{
    .Block() {
        $enumerator = .Call $ints.GetEnumerator();
        .Loop  {
            .If (.Call $enumerator.MoveNext() != False) {
                $i = $enumerator.Current
            } .Else {
                .Break #Label1 { }
            }
        }
        .LabelTarget #Label1:
    }
}

이것은 괜찮은 것처럼 보이지만 그 표현식에서 Compile 을 호출하면 예외가 발생합니다.

"variable 'enumerator' of type 'System.Collections.Generic.IEnumerator`1[System.Int32]' referenced from scope '', but it is not defined"

여기에 정의하지 않았습니까?

    var enumerator = Expression.Variable(typeof(IEnumerator<T>), "enumerator");

?

물론 여기에있는 예제는 인위적으로 아직 실용적이지 않지만, 런타임에 동적으로 결합하기 위해 시체가있는 표현식 트리를 사용하려고합니다.

편집 : 내 초기 문제는 Alexandra에 의해 해결되었다, 감사합니다! 물론, 나는 이제 다음 문제를 겪었다. 변수가있는 BlockExpression 을 선언했습니다. 그 표현식 안에는 그 변수를 참조하는 다른 표현식이 필요합니다. 그러나 표현식이 외부 적으로 제공되기 때문에 그 변수에 대한 실제 참조가 없습니다.

var param = Expression.Variable(typeof(IEnumerable<T>), "something");

var block = Expression.Block(
                new [] { param },
                body
            );

body 변수는 외부에서 전달되며 param 대한 직접적인 참조는 없지만 표현식 ( "something" )의 변수 이름을 알고 있습니다. 다음과 같이 보입니다.

var body = Expression.Call(typeof(Console).GetMethod("WriteLine",new[] { typeof(bool) }), 
               Expression.Equal(Expression.Parameter(typeof(IEnumerable<int>), "something"), Expression.Constant(null)));

이것이 생성하는 "코드"입니다.

.Lambda #Lambda1<System.Action`1[System.Collections.Generic.IEnumerable`1[System.Int32]]>(System.Collections.Generic.IEnumerable`1[System.Int32] $something)
{
    .Block(System.Collections.Generic.IEnumerable`1[System.Int32] $something) {
        .Call System.Console.WriteLine($something== null)
    }
}

그러나 컴파일되지 않습니다. 이전과 같은 오류가 발생했습니다.

TLDR : 표현식 트리에서 식별자로 변수를 참조하려면 어떻게해야합니까?


문제는 매개 변수와 변수를 블록 식에 전달하지 않았다는 것입니다. "내부"표현식에서 사용하지만 블록 표현식은 해당 표현식에 대해 아무것도 모릅니다. 기본적으로 모든 매개 변수와 변수를 블록 표현식에 전달하면됩니다.

        var @foreach = Expression.Block(
            new ParameterExpression[] { item, enumerator, param },
            assignToEnum,
            Expression.Loop(
                Expression.IfThenElse(
                    Expression.NotEqual(doMoveNext, Expression.Constant(false)),
                    assignCurrent,
                    Expression.Break(@break))
            , @break)
        );




expression-trees