7

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

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 operator TestApplication.MyIntClass(TestApplication.MyClass)':不允许用户定义的与基类之间的转换

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

编辑

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

4

9 回答 9

12

如果对象实际上是派生类类型,则只能从基类转换为派生类。我的意思是,您不能将 base ( 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是多少?实际上,从DerivedtoBase转换根本不是转换。它只是告诉编译器让 type 的变量Base指向 type 的对象Derived。这是可能的,因为派生具有更多或相同的特征,而Base反之则不然。

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

于 2009-01-11T19:59:02.960 回答
10

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

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; 
    }
}
于 2009-01-11T20:08:40.257 回答
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(); 基类 bc = (基类)A; ChildClassB B = (ChildClassB)bc;

这将有效地允许将ChildClassA实例强制转换为ChildClassB- 完全错误。

于 2009-01-11T20:29:02.587 回答
0

正如 Mehrdad 所说,你不能让一个物体失望。向上转型是隐式的,因此您不能覆盖它。

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

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

于 2009-01-11T20:05:15.333 回答
0

如前所述,您试图将一个对象转换为不是它派生自的类型。您是否可能想要这样做:

MyClass<int> sth = new MyIntClass(10);
MyIntClass intsth = (MyIntClass) sth;
于 2009-01-11T20:16:32.003 回答
0

尝试使用别名,而不是创建 MyIntClass:

using MyClass<int> = What.Ever.Namespace.MyIntClass;

这现在是有效的:

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

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

于 2009-01-11T20:27:24.867 回答
0

关于你的第二个问题:

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

好吧,假设你有这种情况

class Base {
}

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

你试图这样做

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

应该调用转换吗?当然不是!里面的值x实际上是 type Derived,所以转换是直接的,没有转换。但是如果值不是 type Derived,而是具体的Base,那么用户定义的转换必须发生,否则我们会遇到编译器错误。这一切都毫无意义;用户定义的转换是在编译时找到的,而值的类型x只有在运行时才知道。因此,编译器不知道该怎么做——调用用户定义的转换或简单地转换值......

希望这对你有点意义。

于 2009-01-11T20:41:53.590 回答
0

回答您上次的编辑。

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

MyIntClass intsth = (MyIntClass) sth;

因此,如果保留显式,以下强制转换运算符将是多余的:

公共静态隐式运算符 MyIntClass(MyClass myClass)

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

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

  1. 通过添加显式强制转换,向编译器显示您知道这一点;
  2. 向读者(包括你自己,几分钟后)表明操作可能会失败。
于 2009-01-11T21:59:30.407 回答
0

如果您将赋值或转换视为逐值复制,则基类到派生类的赋值/转换是有意义的。对于新手来说,c# 令人困惑的是它做事的方式不一致:

“int”是一个“简单”类型:

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

'Object' 不是一个简单的类型:

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# 不像其他语言那样工作。

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

于 2009-04-17T16:17:52.420 回答