Programming

.net Func 변환

procodes 2020. 7. 27. 21:36
반응형

.net Func 변환 .net 표현식으로>


메서드 호출을 사용하면 람다에서 표현식으로 쉽게 이동할 수 있습니다.

public void GimmeExpression(Expression<Func<T>> expression)
{
    ((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}

public void SomewhereElse()
{
    GimmeExpression(() => thing.DoStuff());
}

그러나 펑크를 드문 경우에만 표현으로 바꾸고 싶습니다 ...

public void ContainTheDanger(Func<T> dangerousCall)
{
    try 
    {
        dangerousCall();
    }
    catch (Exception e)
    {
        // This next line does not work...
        Expression<Func<T>> DangerousExpression = dangerousCall;
        var nameOfDanger = 
            ((MemberExpression)dangerousCall.Body).Member.Name;
        throw new DangerContainer(
            "Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    ContainTheDanger(() => thing.CrossTheStreams());
}

작동하지 않는 줄은 컴파일 타임 오류를 발생시킵니다 Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'. 명시 적 캐스트는 상황을 해결하지 못합니다. 내가 간과 할 수있는 시설이 있습니까?


아, 전혀 쉽지 않습니다. 식이 아닌 Func<T>일반을 나타냅니다 delegate. 컴파일러에서 수행하는 최적화 및 기타 작업으로 인해 수행 할 수있는 방법이 있으면 일부 데이터가 버려 질 수 있으므로 원래 표현식을 다시 가져 오는 것이 불가능할 수 있습니다 .IL을 즉시 분해합니다. 그리고 표현을 추론하는 것은 결코 쉬운 일이 아닙니다. 람다 식을 데이터 ( Expression<Func<T>>)로 처리하는 것은 컴파일러에서 수행하는 마술입니다 (기본적으로 컴파일러는 코드로 식 트리를 IL로 컴파일하는 대신 코드로 작성합니다).

관련 사실

이것이 람다를 극단으로 밀어주는 언어 (예 : Lisp)가 통역사 로 구현하기 쉬운 이유 입니다. 이러한 언어에서 코드와 데이터는 본질적으로 동일 하지만 ( 런타임 에도 ) 칩은 해당 코드 형식을 이해할 수 없으므로이를 이해하는 인터프리터 (interpreter)를 구축하여 이러한 머신을 에뮬레이트해야합니다. 언어와 같은 Lisp의 선택) 또는 어느 정도 (C #에서 선택한) 힘을 희생 (코드는 더 이상 데이터와 정확히 동일하지 않음)를 희생합니다. C #에서 컴파일러는 컴파일 타임에 람다를 코드 ( Func<T>) 및 데이터 ( Expression<Func<T>>) 로 해석 할 수 있도록하여 코드를 데이터로 취급하는 환상을 제공합니다 .


    private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)  
    {  
        return x => f(x);  
    } 

아마도해야 할 일은 방법을 바꾸는 것입니다. Expression>을 가져 와서 컴파일하고 실행하십시오. 실패하면 이미 식을 살펴보아야합니다.

public void ContainTheDanger(Expression<Func<T>> dangerousCall)
{
    try 
    {
        dangerousCall().Compile().Invoke();;
    }
    catch (Exception e)
    {
        // This next line does not work...
        var nameOfDanger = 
            ((MemberExpression)dangerousCall.Body).Member.Name;
        throw new DangerContainer(
            "Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    ContainTheDanger(() => thing.CrossTheStreams());
}

분명히 이것의 성능에 대한 영향을 고려해야하고 그것이 실제로해야 할 일인지 결정해야합니다.


그러나 .Compile () 메소드를 통해 다른 방법으로 갈 수 있습니다-이것이 유용한 지 확실하지 않습니다.

public void ContainTheDanger<T>(Expression<Func<T>> dangerousCall)
{
    try
    {
        var expr = dangerousCall.Compile();
        expr.Invoke();
    }
    catch (Exception e)
    {
        Expression<Func<T>> DangerousExpression = dangerousCall;
        var nameOfDanger = ((MethodCallExpression)dangerousCall.Body).Method.Name;
        throw new DangerContainer("Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    var thing = new Thing();
    ContainTheDanger(() => thing.CrossTheStreams());
}

때로는 표현이 필요하고 때로는 대리인이 필요한 경우 두 가지 옵션이 있습니다.

  • 다른 방법이 있습니다 (각각 1 개)
  • 항상 Expression<...>버전을 수락하고 .Compile().Invoke(...)대리인을 원할 경우 에만 버전을 수락하십시오 . 분명히 이것은 비용이 든다.

NJection.LambdaConverter 는 대리자를 식으로 변환하는 라이브러리입니다.

public class Program
{
    private static void Main(string[] args) {
       var lambda = Lambda.TransformMethodTo<Func<string, int>>()
                          .From(() => Parse)
                          .ToLambda();            
    }   

    public static int Parse(string value) {
       return int.Parse(value)
    } 
}

 Expression<Func<T>> ToExpression<T>(Func<T> call)
        {
            MethodCallExpression methodCall = call.Target == null
                ? Expression.Call(call.Method)
                : Expression.Call(Expression.Constant(call.Target), call.Method);

            return Expression.Lambda<Func<T>>(methodCall);
        }

Cecil Mono 팀의 JB Evain은이를 가능하게하기 위해 몇 가지 진전을 보이고 있습니다.

http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees


Change

// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;

To

// This next line works!
Expression<Func<T>> DangerousExpression = () => dangerousCall();

참고URL : https://stackoverflow.com/questions/767733/converting-a-net-funct-to-a-net-expressionfunct

반응형