如何将对象转换为自己的类型?

时间:2011-04-05 15:52:09

标签: c# generics casting

我已经阅读了几个关于这个问题的帖子,但没有一个答案似乎适合我。这是情况 - 我有一个通用函数来调用另一个类:

public class Dispatcher<T> where T : Event {
    public void Notify<X>(X tEvent) where X : Event {
        if (someField is IListener<X, T>) {
             //this never executes--X is Event regardless of its derived type
        }
    }
}

和调用代码:

public class Effect {
    public Event myEvent;

    public CallNotify() {
        Dispatcher.Notify(myEvent);
    }
}

问题是Event有几十个派生类型,我需要Notify()调用与派生类型一起发生X.到目前为止,它只调用Notify&lt;事件&gt;()无论我传入什么类型的事件。编译的唯一解决方案是调用Notify(myEvent作为DerivedEvent),但必须对每种类型进行硬编码 - 不可接受。

如果我从派生类的实例中给它一个“this”指针,那么类似的函数会正确推断。

当然,这里有一个使用Reflection的解决方案。

4 个答案:

答案 0 :(得分:2)

你有两个问题。

首先,你的仿制品正在泄漏。每当您需要确定特定实例的类型时,您的函数就不再是通用的。考虑一下你的设计存在缺陷,并重新审视你想要做的事情。

其次,IListener<X,Y>不是一种类型。泛型在.NET中不通用;运行时确定应用程序将需要的所有实际类型并创建它们。例如,如果您在应用程序中实际使用了类型IListener<int,string>,则运行时将创建类型var foo = new List<int>(); var bar = foo.GetType() == typeof(List<>);

bar

在此次考试中,false为{{1}}。

即使有这一切,是的,这是可能的。您只需要了解泛型类型定义的反射是如何工作的。 This is a pretty good link at MSDN that explains how it works.

我强烈建议您以这种方式重新考虑使用泛型。有时抽象广告荒谬并不是最好的事情......

答案 1 :(得分:2)

为了按照您的预期调用它,您必须使用反射来生成myEvent的实际类型的方法。但我要强调的是,执行以下操作是一个不好的想法,可能意味着您的设计需要重新考虑。

MethodInfo openGenericMethod = OtherClass.GetType().GetMethod("Notify");
MethodInfo closedGenericMethod = openGenericMethod.MakeGenericMethod(myEvent.GetType());

closedGenericMethod.Invoke(OtherClass, new object[]{ myEvent });, 

我实际上没有测试上面的代码,但它看起来像那样

答案 2 :(得分:0)

您可以放弃约束并检查以确保它派生自Event,如下所示:

public class Dispatcher<T> where T : Event {
    public void Notify<X>(X tEvent) {
        if(typeof(tEvent).IsSubclassOf(typeof(Event))
        {
            if (someField is IListener<X, T>) {
                //this never executes--X is Event regardless of its derived type
            }
        }
    }
}

答案 3 :(得分:0)

所以我似乎已经找到了另一端的解决方法。而不是

public class Dispatcher<T> where T : Event {
    public void Notify<X>(X tEvent) where X : Event {
        foreach (Object l in listeners) {
            if (l is IListener<X, T>) { //never true
                (l as IListener<X, T>).OnEvent();
            }
        }
    }
}

我有这个混蛋:

public class Dispatcher<T> where T : Event {
    public void Notify<X>(X tEvent) where X : Event {
        foreach (Object l in listeners) {
            foreach (Type t in l.GetType().GetInterfaces()) {
                Type[] temp = t.GetGenericArguments();
                if (temp.Count() > 0 && temp[0] == tEvent.GetType()) {
                    MethodInfo mi = t.GetMethod("OnEvent", new Type[] {tEvent.GetType()});
                    mi.Invoke(l, new object[] { tEvent });
                }
            }
        }
    }
}

这似乎工作,虽然我不喜欢测试每个接口(Windows窗体至少有10个接口)。我打算尝试BrandonAGr的解决方案