如何将对象强制转换为实际类型

时间:2012-11-29 08:37:55

标签: c# .net upcasting

考虑以下代码:

class MyClass
{
}

class MyClass2 : MyClass
{
}

private void Foo(MyClass cl)
{
    //cl is actually MyClass2 instance
    TestGeneric(cl);
}

private void TestGeneric<T>(T val)
{
     //do smth
}

在调用 Foo()后,TestGeneric中的T是 MyClass ,而不是 MyClass2 。 如何将 val 作为 MyClass2 实例进行处理? 提前谢谢。

UPD: 我实际上并不知道该对象是使用 MyClass2 ctor创建的,而是可以通过调用val.GetType()来推断这一点,所以很简单,因为 MyClass2 不会工作

7 个答案:

答案 0 :(得分:5)

可以使用visitor pattern完成。这是一个很好的面向对象的方法,当你在一个处理程序类中(而不是在每个消息中)处理所有代码时,如果需要更多的消息类型,只需添加其他处理程序方法。

// Your message classes
public class MyClass : IMessage
{
    // Implement acceptance of handler:
    public void AcceptHandler(IMessageHandler handler)
    {
        handler.HandleMessage(this);
    }
}

public class MyClass2 : MyClass
{
     // Nothing more here
}

// Define interface of message
public interface IMessage
{
    void AcceptHandler(IMessageHandler handler)
}

// Define interface of handler
public interface IMessageHandler
{
    // For each type of message, define separate method
    void HandleMessage(MyClass message)
    void HandleMessage(MyClass2 message)
}

// Implemente actual handler implementation
public class MessageHandler : IMessageHandler 
{
    // Main handler method
    public void HandleSomeMessage(MyClass message) // Or it could be IMessage
    {
         // Pass this handler to message. Since message implements AcceptHandler
         // as just passing itself to handler, correct method of handler for MyClass
         // or MyClass2 will be called at runtime.
         message.AcceptHandler(this);
    }

    public void HandleMessage(MyClass message)
    {
         // Implement what do you need to be done for MyClass
    }

    public void HandleMessage(MyClass2 message)
    {
         // Implement what do you need to be done for MyClass2
         // If code of MyClass should be run too, just call 
         // this.HandleMessage((MyClass)message);
    }
}

答案 1 :(得分:1)

假设你可以改变Foo而不是它的签名,你可以这样做:

private void Foo(MyClass cl)
{
    TestGeneric((dynamic)cl);
}

这将解析在运行时而不是在编译时调用的TestGeneric版本,在TestGeneric<MyClass2>属于该类型时调用cl

答案 2 :(得分:0)

当您调用泛型方法时,类型参数将根据变量的类型进行解析,而不是基于实际值的类型。

例如,如果你有:

var x = int as object;
Foo(x);

然后你有了这个:

void Foo<T>(T value)
{
}

然后T的类型将是object而不是int,因为它是变量的类型。

一种可能的解决方案是使用反射或编译表达式将值动态转换为最低的子类。

您拥有的其他一些替代方法是使用反射来检查传递的值的实际类型,并将逻辑基于此,或使用其他语言机制,如虚拟方法。

如果您描述了您尝试解决的方案,有人可能会提出合适的解决方案。

答案 3 :(得分:0)

最佳解决方案是将Foo方法更改为通用方法,以便您可以保存类型信息。你应该这样做:

private void Foo<T>(T cl) where T : MyClass
{
    TestGeneric(cl);
}

否则,你会有一个糟糕设计的例子。简单的出路就是

private void Foo(MyClass cl)
{
    if (cl is MyClass2)
        TestGeneric((MyClass2)cl);
    else
        TestGeneric(cl);
}

你也可以使用反射来做更广泛的解决方案,但这会滥用工具来修补糟糕的设计。

以下是一个基于反射的解决方案,但我没有和我一起运行它并尝试修复可能的错误。

private void Foo(MyClass cl)
{
    Type genMethodType = typeof(TestGenericMethodClass);
    MethodInfo genMethod = genMethodType.GetMethod("TestGeneric");
    MethodInfo methodConstructed = genMethod.MakeGenericMethod(cl.GetType());
    object[] args = new object[] { cl };
    methodConstructed.Invoke(instanceOfTestGenericMethodClass, args);
}

所以

  1. 获取定义TestGeneric方法的类类型
  2. 使用Type.GetMethod检索方法定义
  3. 检索cl变量的实际类型以构建通用方法
  4. 使用MethodInfo.MakeGenericMethod构建特定类型的方法
  5. 使用MethodBase.Invoke调用构造方法
  6. 您的代码将根据您当前的实现而有所不同(取决于类型名称,方法可访问性等)。

答案 4 :(得分:0)

(回答评论中的问题)

这是一个庞大的解决方案,你依赖于具体的实现,但你可以做这些事情:

//initialization
Dictionary<Type, Action> typeActions = new Dictionary<Type, Action>();
typeActions.Add(typeof (MyClass), () => {Console.WriteLine("MyClass");});
typeActions.Add(typeof (MyClass2), () => {Console.WriteLine("MyClass2");});

private void TestGeneric<T>(T val)
{
   //here some error checking should be in place, 
   //to make sure that T is a valid entry class
   Action action = typeActions[val.GetType()];
   action();
}

这种方法的缺点是它依赖于MyClass或MyClass2类型的完全变量,所以如果有人后来添加了另一个级别的继承,这将会破坏,但仍然会更灵活而不是通用方法中的if-else或switch。

答案 5 :(得分:0)

当您对对象类型有更多了解,而不是编译器能够从静态代码中推断出来时,就会发现强制转换。

如果您需要更多当前的类型信息,这将为您提供两个选项。

  • 更改声明以使声明信息明确
  • 制作演员

或动态

在你的情况下,第一个要求改变Foo也是通用的

private void Foo<T>(T cl)
{
    //cl is actually MyClass2 instance
    TestGeneric(cl);
}

第二个选项需要转换,我猜你有多种类型,所以你需要很多if-else-if,这通常是一个坏迹象,特别是当条件基于对象的类型时< / p>

private void Foo(MyClass cl)
{
    var mc2 = tcl as MyClass2;
    if(mc2 != null) {
        TestGeneric(mc2);
        return;
    }
    var mc3 = tcl as MyClass3;
    if(mc3 != null) {
        TestGeneric(mc3);
        return;
    }
    throw new InvalidOperationException("Type not recognised");
}

最后你可以动态

private void TestDynamic(dynamic val)
{
    TestGeneric(val);
}

还有其他方法可以动态地执行此操作,例如运行时生成代码,但简单地使用DLR比尝试自己编写代码要容易得多

答案 6 :(得分:0)

您不想在此处调用泛型方法。一旦您输入TestGeneric<T>,即使TMyClass2,您也无法针对MyClass2(甚至MyClass编写任何代码,除非你在T上添加一个限制,所以它没有帮助!

你当然不需要走反射路线或dynamic

最明显的方法是:在类本身中放置特定于类的行为:

class MyClass
{
    public virtual void Test()
    {
        // Behaviour for MyClass
    }
}

class MyClass2 : MyClass
{
    public override void Test()
    {
        // Behaviour for MyClass2
    }
}

private void Foo(MyClass cl)
{
    cl.Test();
}

次佳:分支代码取决于传递的类型:

private void Foo(MyClass cl)
{
    if (cl is MyClass2)
    {
        Test((MyClass2)cl);
    }
    else
    {
        Test(cl);
    }
}

private void Test(MyClass cl)
{
    // Behaviour for MyClass
}

private void Test(MyClass2 cl2)
{
    // Behaviour for MyClass2
}

在这两种情况下,您都可以直接针对MyClass2(或MyClass)编写代码,而无需进行任何反思,使用dynamic或......无论您打算做什么在您的通用方法中 - 分支在typeof(T)