简体   繁体   English

动态转换为通用接口类型

[英]Dynamic casting to a generic interface type

I have read through some of the other answers to do a dynamic cast and I am not sure they address the situation I want to solve hence the question.我已经阅读了其他一些答案以进行动态转换,但我不确定它们是否解决了我想解决的问题,因此出现了这个问题。

I have an interface我有一个界面

public interface ICustomTransmitter<T> : IDataTransmitter where T : EventArgs
{
    event EventHandler<T> DataEvent;
}

and a set of functions that allow me to get the generic type argument at runtime.以及一组允许我在运行时获取泛型类型参数的函数。 This is in order to cast to that type and hook up the specific event (this is thought code so please be gentle)这是为了转换为该类型并连接特定事件(这是思想代码所以请温和)

public bool IsTypeOf(Type baseType, Type interfaceType,
    out Type argumenType)
{
    var interfaces = baseType.GetInterfaces();
    argumenType = null;

    foreach (Type @interface in interfaces)
    {
        if (@interface.Name != interfaceType.Name) continue;
        if (@interface.IsGenericType)
        {
            argumenType = @interface.GetGenericArguments()[0];
        }
        return true;
    }
    return false;
}

And the function that uses the magic above以及使用上面魔法的 function

Type argument;
var generic = typeof (ICustomTransmitter<>);
if (IsTypeOf(receiver.GetType(),generic ,out argument))
{
    var created = generic.MakeGenericType(new[] {argument});

    //the line of code missing is below
    receiver as created 
}

Is it possible to cast the receiver to that created type?是否可以将接收器转换为创建的类型? Also I need to have a solution that works in both dot.net 3.5 and dot.net 4.此外,我需要一个适用于 dot.net 3.5 和 dot.net 4 的解决方案。

There is no kind of casting that will do that. 没有那种铸造可以做到这一点。 Casting is about checking if a runtime type is a known compile time type. 强制转换是关于检查运行时类型是否为已知的编译时类型。 You don't even have a compile time type. 您甚至没有编译时间类型。

What you need to do is use reflection to look for the interface, extract the generic type argument, create a compatible delegate, and hook up the handler for the delegate. 您需要做的是使用反射来查找接口,提取泛型类型参数,创建兼容的委托并为委托连接处理程序。 You've got the first couple steps with IsOfType . 您已经有了IsOfType的前几个步骤。

When you hook up an event handler you are calling its add method. 连接事件处理程序时,您将调用其add方法。 The compiler generates a name for this method that is of the form "add_EventName". 编译器为此方法生成一个名称,格式为“ add_EventName”。 Here is some sample code that does all that stuff: 这是完成所有这些工作的一些示例代码:

using System;
using System.Reflection;

public class Program
{
    public static void Main(string[] args)
    {
        object o = new CustomArgsTransmitter();

        // make sure we've got the interface
        var interf = o.GetType().GetInterface("ICustomTransmitter`1");

        // get the arg type.
        var argType = interf.GetGenericArguments()[0];

        // create a delegate for the handler based on the arg type above
        var handlerMethodInfo = typeof(Program).GetMethod("Handler", BindingFlags.Static | BindingFlags.Public)
        var del = Delegate.CreateDelegate(typeof(EventHandler<>).MakeGenericType(argType), handlerMethodInfo);

        // Invoke the add method of the event.
        o.GetType().InvokeMember("add_DataEvent", BindingFlags.InvokeMethod, null, o, new object[] { del });

        //  just test code at this point.
        // fire event to make sure it is signed up.
        // It should print a message to the console.
        ((CustomArgsTransmitter)o).FireEvent();

    }

    public static void Handler(object sender, EventArgs e)
    {
        Console.WriteLine("Got event {0} from {1}", e, sender);
    }
}

public interface IDataTransmitter { }

public interface ICustomTransmitter<T> : IDataTransmitter where T : EventArgs
{
    event EventHandler<T> DataEvent;
}

public class MyArgs : EventArgs { }

public class CustomArgsTransmitter : ICustomTransmitter<MyArgs>
{
    public event EventHandler<MyArgs> DataEvent;

    public void FireEvent()
    {
        DataEvent(this, new MyArgs());
    }
}

No. You cannot cast an expression to a type that is not known at compile time. 不能。您不能将表达式强制转换为编译时未知的类型。 (By "known", I mean resolvable to a Type whose generic type parameters are closed.) (通过“已知”,我的意思是可解析为通用类型参数已关闭的Type 。)

Having said that, I think it may be possible by using the expression API. 话虽如此,我认为使用表达式API可能是可行的。 The idea is that you would build a lambda expression of the type you determined (which can be strongly-typed), compile it, then execute it on your object to perform the cast. 这个想法是,您将构建一个确定类型的lambda表达式(可以是强类型的),对其进行编译,然后在您的对象上执行以执行转换。 If you 100% need to do this, that's the direction I'd look. 如果您100%需要这样做,这就是我要寻找的方向。

Using reflection you can cast from a type which is known only at runtime.使用反射,您可以从仅在运行时已知的类型进行转换。 By using this pattern - calling a generic method through reflection - you get into a method in which the generic parameter or any class using it is defined.通过使用这种模式——通过反射调用泛型方法——你进入了一个方法,其中定义了泛型参数或任何使用它的 class。

Here is some code using that pattern for the case above but it can be used in every case where a type or its generic parameter(s) are only known at runtime.以下是针对上述情况使用该模式的一些代码,但它可用于类型或其泛型参数仅在运行时已知的所有情况。 One has to be careful of the cost of using reflection OFC.必须注意使用反射 OFC 的成本。

    public void CastToInterfaceTest()
    {
        var args = new CustomEventArgs { Data = "whatever" };
        var instance = new CustomTransmitter();
        ProcessGenericEvent(instance, args);
    }

    public void ProcessGenericEvent(ICustomTransmitter instance, EventArgs args)
    {
        static MethodInfo AddEventHandlerMethodInfo() =>
            typeof(ReflectionCastHelper).GetMethod(nameof(AddEventHandler),
                BindingFlags.NonPublic | BindingFlags.Static);

        var t = instance.GetType();
        var interfaceType = t.GetInterfaces()
            .Single(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICustomTransmitter<>));
        var genericType = interfaceType.GenericTypeArguments[0];

        AddEventHandlerMethodInfo()
            .MakeGenericMethod(genericType)
            .Invoke(null, new object[] { instance, args });
    }

    private static void AddEventHandler<TEventArgs>(object instance, TEventArgs args)
        where TEventArgs : EventArgs, new()
    {
        var i = instance as ICustomTransmitter<TEventArgs>;
        i!.Event += i!.CustomHandler;
        i!.RaiseEvent(args);
    }

    public interface ICustomTransmitter
    {
    }

    public interface ICustomTransmitter<T> : ICustomTransmitter
        where T : EventArgs
    {
        event EventHandler<T> Event;
        void RaiseEvent(T eventArgs);
        void CustomHandler(object sender, T e);
    }

    public class CustomEventArgs : EventArgs
    {
        public string Data { get; set; }
    }

    public class CustomTransmitter : ICustomTransmitter<CustomEventArgs>
    {
        public event EventHandler<CustomEventArgs> Event;
        public void RaiseEvent(CustomEventArgs eventArgs) => Event!(this, eventArgs);
        public void CustomHandler(object sender, CustomEventArgs e) => 
            Console.WriteLine($"Got event {e} with data \"{e.Data}\" from {sender}");
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM