反思:如何在C#7.0中找到并调用本地功能?

时间:2017-04-11 13:59:01

标签: c# methods reflection system.reflection

我有一个私有的静态通用方法,我想用反射调用,但实际上我想要'捆绑'它在另一种方法里面。 C#7.0支持本地功能,所以这绝对是可能的。

你会说"为什么不直接给它打电话?"但我使用它来获得以强类型方式使用对象和System.Type的能力,所以我需要动态调用它。如果我拥有它作为自己的私有静态泛型方法,那么这段代码已经有效了。

private static void HandleResponse(object data, Type asType)
{
    var application = typeof(Program);

    application
        .GetMethod(nameof(UseAs), BindingFlags.Static | BindingFlags.NonPublic)
        .MakeGenericMethod(asType)
        .Invoke(null, new object[] { data });
}

public static void UseAs<T>(T obj)
{
    Console.WriteLine($"Object is now a: {typeof(T)}:");
};

以上代码有效。如果我传入:

data: new TestObject(),
type: typeof(TestObject)

我实际上在UseAs中有一个TestObject。

所以,我想把这一切都放在一个方法中,如下所示:

private static void HandleResponse(object data, Type asType)
{
    void useAs<T>(T obj)
    {
        Console.WriteLine($"Object is now a: {typeof(T)}:");
    };

    var application = typeof(Program);

    application
        .GetMethod(nameof(UseAs), BindingFlags.Static | BindingFlags.NonPublic)
        .MakeGenericMethod(asType)
        .Invoke(null, new object[] { data });
}

不幸的是,GetMethod代码不再有效。我听说在编译时编译器将任何本地函数转换为静态方法,所以我弹出到即时窗口并运行:

application.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)

......而且,我确实看到了这个回应:

{System.Reflection.MethodInfo[3]}
    [0]: {Void Main(System.String[])}
    [1]: {Void HandleResponse(System.Object, System.Type)}
    [2]: {Void <HandleResponse>g__useAs1_0[T](T)}

这是列表中的最后一个方法。有没有人知道如何以合理的方式访问这样的方法?

谢谢!

编辑:

我确实可以将UseAs用作普通的私有静态方法。它不会被其他任何地方使用,所以我想&#34;打包&#34;一切都在一个方法中。

另外,这实际上应该是关于一般地查找局部函数的问题,并且在StackOverflow上的任何其他地方似乎都没有关于它的问题。我觉得很难相信,在某些方面,至少有人不会好奇这样做。

我首先提供任何代码都犹豫不决,因为我只是在修改一个想法,但我试图完成的实际目标完全是问题的次要。

3 个答案:

答案 0 :(得分:8)

好的,我有一个解决方案。但它确实可怕。它涉及使用特定类型从您的方法创建委托,然后使用它来查找泛型方法,然后构建另一个特定方法并调用它。

所以我们从UseAs<int>转到UseAs<T>UseAs<the-type-we-want>

它可能在很多方面都出现了可怕的错误,但它适用于我测试的非常有限的样本:

// DISCLAIMER: THIS CODE IS FAIRLY HACKY, AND MAY WELL FAIL IN WEIRD
// SITUATIONS. USE WITH EXTREME CAUTION AND LOTS OF TESTS!

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        HandleResponse("foo", typeof(string));
    }

    static void HandleResponse(object data, Type type)
    {
        string local = "This was a local variable";
        void UseAs<T>(T obj)
        {
            Console.WriteLine($"Object is now a: {typeof(T)}:");
            // Proof that we're capturing the target too
            Console.WriteLine($"Local was {local}");
        }

        InvokeHelper(UseAs, data, type);
    }

    // This could be in any class you want
    static void InvokeHelper(Action<int> int32Action, object data, Type type)
    {
        // You probably want to validate that it really is a generic method...
        var method = int32Action.Method;
        var genericMethod = method.GetGenericMethodDefinition();
        var concreteMethod = genericMethod.MakeGenericMethod(new[] { type });
        concreteMethod.Invoke(int32Action.Target, new[] { data });
    }
}

答案 1 :(得分:6)

使用反射调用本地函数就像找麻烦一样。该名称不是“固定的”。它根据同一个类中有多少其他本地函数而改变...所以如果你修改另一个方法,你可以改变你感兴趣的本地函数的名称。

您可以查看此TryRoslyn

有三个类,Class1Class2Class3。它们都有一个方法M,内部有一个本地函数TestClass1Class2与最后一个字符相同。然后将本地方法编译为名为<M>g__Test0_0()的方法。 Class3M方法之前引入了另一个方法Filler,另一个本地函数(Foo)然后编译为<Filler>g__Foo0_0。在这种情况下,M的本地方法名为<M>g__Test1_0()

答案 2 :(得分:0)

我知道这是一个老问题了-但我试图理解为什么HandleResponse本身本身不能成为通用方法?您可以将类型传递给本地函数,该函数本身可以使用相同的外部类型。这消除了反射的需要。这意味着将使用不同的类型在每次调用时生成新的方法-但我不知道如何避免编译和执行代码中的这一步-UseAs包含在内是通用的。所以这就是我的想法。

using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;


[TestClass]
public  class testy
{
    class a { }
    class b : a { }

    [TestMethod]
    public void caller()
    {
        HandleResponse<a>(new b());
    }

    private static void HandleResponse<T>(object data)
    {
        UseAs((T)data);

        void UseAs(T obj)
        {
            System.Diagnostics.Debug.WriteLine($"Object is now a: {typeof(T)}:");
        }
    }
}

我假设您实际上是在试图将一种类型转换为另一种有效类型。在这种情况下,您的代码将不是类型安全的,并且可能会因“ obj无法转换为DateTime类型”而失败。我的解决方案没有什么不同。我假设您可以将对象转换为T。我没有检查它是否适合该类型(在这种情况下有效使用反射)。因此,我相信我的小解决方案在功能上是等效的。