투명 프록시 인스턴스로 메서드 호출 할 때 ref 파라미터 적용 안되는 원인이 궁금합니다.

아래 예제 코드가 있습니다.
“test” 문자열로 초기화한 지역변수
a, sb, b 3개가 있는데요

아래 코드를 실행했을 때 할당되는 값은 아래와 같습니다.
a → “test”
sb → {test/add}
b → “test/add”

a만 문자열이 변경되지 않고 그대로 있습니다.

a와 b의 차이점은 투명 프록시 인스턴스를 사용했냐 안했냐의 차이 같은데

  1. 투명 프록시 인스턴스를 사용하면 무엇 때문에 이렇게 동작하는 지 궁금합니다.

  2. 같은 투명 프록시 인스턴스를 사용했음에도 StringBuilder 객체는 값이 변경되었습니다. 클래스 객체라서 call by reference로 동작하는 건 알지만 string을 ref로 하는 것과 무슨 차이가 있는 건지 궁금합니다.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Text;
using System.Threading.Tasks;

namespace InvokeTest
{
public class TraceProxy : RealProxy
{
private readonly T _decorated;

    public TraceProxy(T decorated)
      : base(typeof(T))
    {
        _decorated = decorated;
    }

    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;
        var methodInfo = methodCall?.MethodBase as MethodInfo;            
        try
        {
            var result = methodInfo?.Invoke(_decorated, methodCall.InArgs);
            return new ReturnMessage(result, null, 0, methodCall?.LogicalCallContext, methodCall);
        }
        catch (Exception e)
        {
            return new ReturnMessage(e, methodCall);
        }
    }
}

public class Test : MarshalByRefObject
{
    public void RefTest(ref string a)
    {
        a += "/add";
    }

    public void SBTest(StringBuilder sb)
    {
        sb.Append("/add");
    }
}

public class Proxy
{
    public static T Create<T>(T instance)
    {
        var dynamicProxy = new TraceProxy<T>(instance);
        return (T)dynamicProxy.GetTransparentProxy();
    }
}


class Program
{
    static void Main(string[] args)
    {
        var t = Proxy.Create<Test>(new Test());
        string a = "test";
        string b = "test";
        StringBuilder sb = new StringBuilder("test");
        t.RefTest(ref a);
        t.SBTest(sb);

        Test t2 = new Test();
        t2.RefTest(ref b)
        ;
    }
}

}

2개의 좋아요

Proxy 에서 Invoke 이후 결과 메세지를

클라이언트 측 에서 전달한 string 객체 (inArg)에 대한 결과 out 파라메터로 사용하지 않아서 입니다.

다음 처럼 코드를 변경해 보시면 원하는 기대 값을 얻을 수 있습니다.


[Invoke() 메서드 일부중]

var args = methodCall.InArgs;  // 클라이언트에서 전달 받은 파라메터
var result = methodInfo?.Invoke(_decorated, args);

// ReturnMessage의 두번째 인자가 out으로 전달할 파라메터 정보
return new ReturnMessage(result, args, args.Length, methodCall?.LogicalCallContext, methodCall);

추가로 질문에 대한 내용은 아니지만 혹시나 AOP 같은 적용을 위해 프록시 사용으로
메서드를 호출 하는 거라면 프록시 방식 보단 IL코드 위빙 방식이 성능이나 구현 방법이나 더 깔끔하고 좋습니다.

4개의 좋아요

답변 감사합니다.

사실 디자인 패턴도 잘 모르고 AOP도 몇 번 개념을 읽긴 했었는데
여전히 잘 모르네요.

회사 소스코드 분석하다가 도대체 왜 ref 가 안 먹히나 해서
질문 올리려고 비슷하게 샘플 코드 짜서 올려봤습니다.

2개의 좋아요

var result = methodInfo?.Invoke(_decorated, args);

// ReturnMessage의 두번째 인자가 out으로 전달할 파라메터 정보
return new ReturnMessage(result, args, args.Length, methodCall?.LogicalCallContext, methodCall);

답변해주신 이 내용에서

실제 함수를 호출하는 부분이
methodInfo?.Invoke(_decorated, args);

아니라
return new ReturnMessage(result, args, args.Length, methodCall?.LogicalCallContext, methodCall);

이 것인가요?

2개의 좋아요

실제 메서드 호출은 methodInfo?.Invoke(_decorated, args); 부분이 맞고

파라메터의 out 결과를

ReturnMessage의 두번째 인자로 전달해야 한다는 것입니다.

3개의 좋아요

아, 함수 실행은 따로 하고
함수 실행 결과를 또 따로 반환하는 형식인가 보네요.
함수 실행과 결과 반환을 분리해서 진행하는 것이군요.
감사합니다.

2개의 좋아요

네 메서드의 실행 결과는 프록시 내부에서 처리 된 것이므로

그 결과의 반환 정보와 각 파라메터의 out 정보 또한

다시 클라이언트에게 메세지 형태로 전달해 줘야 하죠

3개의 좋아요