C# - 泛型和继承问题

时间:2009-01-11 19:52:51

标签: c# generics inheritance casting

我遇到了继承和泛型问题。 这是解释我的问题的代码:

namespace TestApplication
{
    public class MyClass<T>
    {
        private T field;

        public MyClass(T field)
        {
            this.field = field;
        }
    }

    public class MyIntClass : MyClass<int>
    {
        public MyIntClass(int field)
            : base(field)
        {
        }
    }
}

当我尝试做这样的事情时:

MyClass<int> sth = new MyClass<int>(10);
MyIntClass intsth = (MyIntClass) sth;

我收到强制转换异常:无效的强制转换异常。无法将'TestApplication.MyClass`1 [System.Int32]'强制转换为'TestApplication.MyIntClass'。

更有甚者,我无法创建演员:

public static implicit operator MyIntClass(MyClass<int> myClass)

因为:'TestApplication.MyIntClass.implicit运算符TestApplication.MyIntClass(TestApplication.MyClass)':不允许在基类之间进行用户定义的转换

我需要如上所述创建演员表。我不知道为什么我不能从基类类型转换。我怎么解决这个问题? 提前谢谢。

修改

感谢您的回答。 现在我看到我无法从基类转换为派生类,我发现它与泛型没有任何关系。 但为什么我不能从基类创建用户定义的转换?我有一个返回基类的方法。我能够定义一个转换方法,但创建一个转换操作符imho将是一个更好的解决方案。

9 个答案:

答案 0 :(得分:12)

如果对象实际上是派生类的类型,则只能从基类强制转换为派生类。我的意思是,您无法将基础(MyClass<int>)的实例强制转换为MyIntClass。但是,如果它实际上是MyIntClass类型存储为MyClass<int>实例,则可以将其强制转换。

MyClass<int> foo = new MyIntClass();
MyIntClass bar = (MyIntClass)foo; // this works.

假设:

class Base {
   int x;
}

class Derived : Base {
   int y;
}

Base foo = new Base();
Derived bar = (Derived)foo;

如果允许,bar.y的价值是多少? 事实上,从Derived转换为Base根本不是转换。它只是告诉编译器让类型Base的变量指向类型为Derived的对象。有可能因为派生的特征比Base更多或相同,而在另一种情况下并非如此。

如果能够在基类和派生类之间创建转换运算符,则C#编译器将无法将其与为它们定义的内置关系区分开来。这就是为什么你不能在继承层次结构中创建转换操作符的原因。

答案 1 :(得分:10)

到目前为止,其他答案都是正确的,但我想指出您的示例 nothing 与泛型有关。它相当于:

using System;

class Base {}
class Child : Base {}

class Test
{
    static void Main()
    {
        Base b = new Base();

        // This will throw an exception
        Child c = (Child) b; 
    }
}

答案 2 :(得分:2)

在评论中你问:

  

但是为什么不允许从基类转换?

简单 - 这没有任何意义。考虑一下这个例子:

class BaseClass
{
    public int x;

    public BaseClass(int StartX)
    {
        this.x = StartX;
    }
}
class ChildClass: BaseClass
{
    public int Y;

    public BaseClass(int StartX, StartY): base(StartX)
    {
        this.y = StartY;
    }
}

class Program
{
    public static void Main()
    {
        BaseClass B = new BaseClass(3);
        ChildClass C = (ChildClass)B;
        Console.WriteLine(C.y);
    }
}

假设演员工作,你认为这个节目会输出什么?更糟糕的是 - 假设BaseClass有两个子类 - ChildClassAChildClassB。你想要这个吗?

ChildClassA A = new ChildClassA();    BaseClass bc =(BaseClass)A;    ChildClassB B =(ChildClassB)bc;

这有效地允许将ChildClassA个实例投射到ChildClassB - 完全错误。

答案 3 :(得分:0)

正如迈赫达德所说,你不能贬低一个物体。上传是隐含的,因此您无法覆盖它。

对于隐式运算符,您仍然可以在派生类中创建一个构造函数,该构造函数接收类型为baseclass的参数。

如果需要自由转换,请将变量定义为基类,但实例化派生类。

答案 4 :(得分:0)

如前所述,您正在尝试将对象转换为不是从中派生出来的类型。您是否想要这样做:

MyClass<int> sth = new MyIntClass(10);
MyIntClass intsth = (MyIntClass) sth;

答案 5 :(得分:0)

尝试使用别名:

,而不是创建MyIntClass
using MyClass<int> = What.Ever.Namespace.MyIntClass;

现在有效:

MyClass<int> foo = new MyClass<int>();
MyIntClass bar = (MyIntClass)foo;

只需了解在执行using别名时,您必须在别名类型名称(What.Ever.Namespace)上限定命名空间。

答案 6 :(得分:0)

关于你的第二个问题:

  

但为什么我不能从基类创建用户定义的转换?

好吧,假设你有这个场景

class Base {
}

class Derived {
    public static operator Derived(Base b) { ... }
}

你试图这样做

Base x = new Derived();
Derived y = (Derived)x;

应该调用转换吗?当然不是! x内的值实际上是Derived类型,因此转换是直接的,没有转换。但是如果值不是Derived类型,而是具体的Base,则必须进行用户定义的转换,否则我们会遇到编译器错误。这一切都没有意义;用户定义的转换在编译时找到,x的值类型仅在运行时已知。因此,编译器不知道该怎么做 - 调用用户定义的转换或只是转换值...

希望这对你有点意义。

答案 7 :(得分:0)

回答你的上次修改。

此代码已经编译,它只在运行时失败:

  

MyIntClass intsth =(MyIntClass)sth;

因此,如果明确指出,以下强制转换操作符将是多余的:

  

public static implicit operator MyIntClass(MyClass myClass)

因此,编译器应该阻止您添加该转换。我认为这个错误可能会让人感到困惑,但我认为如果B是从A派生的话,它只是禁止将B类转换为A类(我觉得这个警告似乎是为了防止任何转换为​​A,起初)。

如果操作符是隐式的,那也是危险的,因为向下转换总是会失败,所以你必须:

  1. 通过添加显式强制转换来显示您知道的编译器;
  2. 显示读者(包括你自己,几分钟后)操作可能会失败。

答案 8 :(得分:0)

如果将赋值或转换视为值副本值,则将基类赋值/转换为派生类是有意义的。有什么令人困惑的c#for 新手是做事的不一致方式:

'int'是一个'简单'类型:

int i = 5;   // <- this creates an int.  
int j = i;    // <- this creates another int and copies the value of i into j.

'对象'不是一个简单的类型:

Object a;                            // <- this does not create a copy of 'Object', only a reference to one
Object b = new Object();   // <- this actually creates an Object
a = b;                                 // <- this sets the reference to an object a to the reference to an object b.
                                          // both of them reference the same Object. No values were copied.

如果它正在复制值,那么将基类复制到派生类就行了 工作。 C#不像其他语言一样工作。

我认为这可能让你感到困惑。