Func를 얻을 수 있습니까? (또는 유사) MethodInfo 개체에서?
일반적으로 리플렉션을 사용하면 성능에 영향을 미친다는 것을 알고 있습니다. (저는 사실 성찰의 팬이 아닙니다. 이것은 순전히 학문적 인 질문입니다.)
다음과 같은 클래스가 있다고 가정합니다.
public class MyClass {
public string GetName() {
return "My Name";
}
}
여기서 참아주세요. MyClass
called 인스턴스가 있으면을 호출 x
할 수 있습니다 x.GetName()
. 또한 Func<string>
변수를 x.GetName
.
이제 여기 내 질문이 있습니다. 의 내가 생각한 하지 않습니다 위의 클래스라고 알고 MyClass
; 나는 물건을 가지고 x
있지만 그것이 무엇인지 모르겠다. 다음 GetName
을 수행 하여 해당 객체에 메서드 가 있는지 확인할 수 있습니다 .
MethodInfo getName = x.GetType().GetMethod("GetName");
getName
null이 아니라고 가정합니다 . 그 때 나는 또한 확인할 수없는 경우 getName.ReturnType == typeof(string)
와 getName.GetParameters().Length == 0
,이 시점에서, 나는 나의하여 방법을 표현하는 것이 매우 확신 할 수없는 것 getName
오브젝트 수 확실히 A를 캐스팅 할 Func<string>
어떻게 든?
나는이 있다는 것을 알고 있으며 MethodInfo.Invoke
항상 다음 과 같이 만들 수 있다는 것도 알고 있습니다Func<string>
.
Func<string> getNameFunc = () => getName.Invoke(x, null);
나는 내가 무엇을 요구하고있어 갈 수있는 방법이 있는지 추측 에서MethodInfo
객체 에 에 반사의 성능 비용을 들이지, 그것이 표현하는 실제 방법 프로세스가 있지만, 후 , 예를 통해 (직접 메소드를 호출 할 수있는 그 시점 a Func<string>
또는 유사한 것) 성능 저하 없이 .
내가 구상하는 것은 다음과 같이 보일 수 있습니다.
// obviously this would throw an exception if GetActualInstanceMethod returned
// something that couldn't be cast to a Func<string>
Func<string> getNameFunc = (Func<string>)getName.GetActualInstanceMethod(x);
(나는 존재하지 않는 실현, 아무것도 만약 거기에 궁금하네요 같은 .이)
이것은 약간 더 긴 경로이지만 빠른 메서드 호출을 제공하고 다른 답변과 달리 다른 인스턴스를 통과 할 수 있기 때문에 이전 답변을 대체합니다 (여러 인스턴스가 발생할 경우 같은 유형의). 원하지 않는 경우 하단의 업데이트를 확인하거나 Ben M의 답변을 확인하십시오.
다음은 원하는 작업을 수행하는 테스트 방법입니다.
public class TestType
{
public string GetName() { return "hello world!"; }
}
[TestMethod]
public void TestMethod2()
{
object o = new TestType();
var input = Expression.Parameter(typeof(object), "input");
var method = o.GetType().GetMethod("GetName",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
//you should check for null *and* make sure the return type is string here.
Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string)));
//now build a dynamic bit of code that does this:
//(object o) => ((TestType)o).GetName();
Func<object, string> result = Expression.Lambda<Func<object, string>>(
Expression.Call(Expression.Convert(input, o.GetType()), method), input).Compile();
string str = result(o);
Assert.AreEqual("hello world!", str);
}
델리게이트를 한 번 빌드하면 사전에 캐시 할 수 있습니다.
Dictionary<Type, Func<object, string>> _methods;
그런 다음 들어오는 객체의 Type (GetType ()에서)을 키로 사용하여 사전에 추가하기 만하면됩니다. 앞으로는 먼저 사전에 준비된 델리게이트가 있는지 확인하고 (있는 경우 호출) 그렇지 않으면 먼저 빌드하고 추가 한 다음 호출합니다.
부수적으로 이것은 DLR이 동적 디스패치 메커니즘에 대해 수행하는 작업의 매우 단순화 된 버전입니다 (C # 용어에서 'dynamic'키워드를 사용하는 경우).
그리고 마지막으로
몇몇 사람들이 언급했듯이받은 객체에 직접 바인딩 된 Func를 굽고 싶다면 다음과 같이하십시오.
[TestMethod]
public void TestMethod3()
{
object o = new TestType();
var method = o.GetType().GetMethod("GetName",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string)));
//this time, we bake Expression.Constant(o) in.
Func<string> result = Expression.Lambda<Func<string>>(
Expression.Call(Expression.Constant(o), method)).Compile();
string str = result(); //no parameter this time.
Assert.AreEqual("hello world!", str);
}
하지만 표현식 트리가 삭제되면 해당 트리가 (Ben M의 댓글로 인해 삭제됨)o
범위 내에 있는지 확인해야합니다.
그렇지 않으면 불쾌한 결과를 얻을 수 있습니다. 가장 쉬운 방법은 델리게이트의 수명 동안 로컬 참조 (아마도 클래스 인스턴스에서)를 유지하는 것입니다.
예, 가능합니다.
Func<string> func = (Func<string>)
Delegate.CreateDelegate(typeof(Func<string>), getName);
표현 트리를 구축하여 내 대답은 다음과 같습니다. 다른 답변과 달리 결과 ( getNameFunc
)는 매개 변수로 전달할 필요없이 원래 인스턴스에 바인딩 된 함수입니다.
class Program
{
static void Main(string[] args)
{
var p = new Program();
var getNameFunc = GetStringReturningFunc(p, "GetName");
var name = getNameFunc();
Debug.Assert(name == p.GetName());
}
public string GetName()
{
return "Bob";
}
static Func<string> GetStringReturningFunc(object x, string methodName)
{
var methodInfo = x.GetType().GetMethod(methodName);
if (methodInfo == null ||
methodInfo.ReturnType != typeof(string) ||
methodInfo.GetParameters().Length != 0)
{
throw new ArgumentException();
}
var xRef = Expression.Constant(x);
var callRef = Expression.Call(xRef, methodInfo);
var lambda = (Expression<Func<string>>)Expression.Lambda(callRef);
return lambda.Compile();
}
}
이를 수행하는 가장 쉬운 방법은 다음과 Delegate.CreateDelegate
같습니다.
Func<string> getNameFunc = (Func<string>) Delegate.CreateDelegate(
typeof(Func<string>), x, getName);
이 바인딩이 있음을 참고 getNameFunc
하기 x
때문에 각 x
새 대리자 인스턴스를 작성해야합니다 것입니다. 이 옵션은 Expression
기반 예제 보다 훨씬 덜 복잡합니다 . 그러나 식 기반 예제를 사용하면 Func<MyClass, string> getNameFuncForAny
한 번만 만들 수 있으며 MyClass
.
이러한 getNameFuncForAny를 만들려면 다음과 같은 메서드가 필요합니다.
public Func<MyClass, string> GetInstanceMethod(MethodInfo method)
{
ParameterExpression x = Expression.Parameter(typeof(MyClass), "it");
return Expression.Lambda<Func<MyClass, string>>(
Expression.Call(x, method), x).Compile();
}
다음과 같이 사용할 수 있습니다.
Func<MyClass, string> getNameFuncForAny = GetInstanceMethod(getName);
MyClass x1 = new MyClass();
MyClass x2 = new MyClass();
string result1 = getNameFuncForAny(x1);
string result2 = getNameFuncForAny(x2);
에 묶이지 않으려면 Func<MyClass, string>
다음을 정의 할 수 있습니다.
public TDelegate GetParameterlessInstanceMethod<TDelegate>(MethodInfo method)
{
ParameterExpression x = Expression.Parameter(method.ReflectedType, "it");
return Expression.Lambda<TDelegate>(
Expression.Call(x, method), x).Compile();
}
You could build an Expression Tree representing a lambda calling this method and then Compile()
it so that further calls will just be as fast as standard compiled calls.
Alternatively, I wrote this method a good while ago based on a great MSDN article, which generates a wrapper using IL to call any MethodInfo
way faster than with MethodInfo.DynamicInvoke
since once the code is generated, there are almost no overhead over a normal call.
One off the top of my head approach would be to use dynamic. You could then so something like this:
if( /* This method can be a Func<string> */)
{
dynamic methodCall = myObject;
string response = methodCall.GetName();
}
ReferenceURL : https://stackoverflow.com/questions/2933221/can-you-get-a-funct-or-similar-from-a-methodinfo-object
'IT TIP' 카테고리의 다른 글
SQLite를 사용하는 LINQ (linqtosql) (0) | 2020.12.25 |
---|---|
피어 투 피어 : 피어를 찾는 방법 (0) | 2020.12.25 |
Java, BigDecimal의 소수 부분 만 추출 하시겠습니까? (0) | 2020.12.15 |
자바 스크립트에서 긴 숫자를 축약 된 문자열로 변환합니다. (0) | 2020.12.15 |
AttributeError : Python에서 속성을 설정할 수 없습니다. (0) | 2020.12.15 |