【问题标题】:Are implicity/explicit conversion methods inherited in C#?C# 中是否继承了隐式/显式转换方法?
【发布时间】:2009-06-09 00:16:13
【问题描述】:

我不确定我在这里做错了什么。我有一个泛型类,它基本上是一个美化的整数,有一些用于某些字符串格式的方法,以及 in/from 字符串和 int 转换:

public class Base
{
    protected int m_value;
    ...
    // From int
    public static implicit operator Base(int Value)
    {
        return new Base(Value);
    }
    ...
    // To string
    public static explicit operator string(Base Value)
    {
        return String.Format("${0:X6}", (int)Value);
    }
}

而且它运行良好。我可以成功使用隐式和显式转换:

Base b = 1;
Console.WriteLine((string)b);  // Outputs "$000001", as expected.

然后我从这个类派生不同的子类,它们打开/关闭 m_value 中不同的命名位。例如:

public class Derived : Base
{

}

然后我不能使用我的隐式到/从 int 转换:

Derived d = 3;
// Cannot implicitly convert type 'int' to 'Derived'. An explicit conversion exists (are you missing a cast?)

即使这样也会出现同样的错误:

Derived d = (int)3;

派生类中是否没有继承隐式/显式转换?如果没有,这将需要大量的代码复制。

回应 非常感谢您的快速回复!你们都应该得到“答案”复选标记,它们都是非常好的答案。关键是要考虑等号两边的类型。现在我这么想,这完全有道理。

我显然只需要重写我的“到派生”转换。 “到 Int32、String 等”的转换仍然适用。

【问题讨论】:

标签: c# inheritance


【解决方案1】:

重载的运算符(包括转换)是继承的,但并非以您期望的方式继承。特别是,它们并没有神奇地扩展为对派生类型进行操作。让我们再看看你的代码:

Derived d = 3;

此时,编译器有两个类可供使用,System.Int32 和 Derived。所以它在这些类中寻找转换运算符。会考虑Derived从Base继承的那些,即:

public static implicit operator Base(int Value);
public static explicit operator string(Base Value);

第二个不匹配,因为参数应该是 Int32。第一个确实匹配参数,但它的返回类型是 Base,而那个不匹配。另一方面,你可以这样写:

string s = (string) new Derived();

而且它会起作用,因为 Base 中的显式运算符存在并且适用(参数应为 Base,Derived 继承自 Base,因此与参数类型匹配)。

编译器不会自动“正确”地重新定义派生类型中的转换运算符的原因是因为没有通用的方法来这样做。

【讨论】:

    【解决方案2】:

    虽然您不能直接执行此操作,但您可以使用一些捷径来启用代码重用。

    public class Base
    {
        protected int m_value;
        ...
        // From int
        public static implicit operator Base(int Value)
        {
            Base newObject = new Base();
            newObject.FromInt(Value);
            return newObject;
        }
        ...
        // To string
        public static explicit operator string(Base Value)
        {
            return String.Format("${0:X6}", (int)Value);
        }
    
        //Instance FromInt()
        protected void FromInt(int value)
        {
            m_value = value;
        }
    }
    
    public class Derived : Base
    {
        // To string, re-use Base-ToString
        public static explicit operator string(Derived Value)
        {
            // Cast (Derived)Value to (Base)Value and use implemented (Base)toString
            return (string)(Base)Value;
        }
    
        // Use FromInt, which has access to instance variables
        public static implicit operator Base(int Value)
        {
            // We can't use (Base)obj = Value because it will create
            // a "new Base()" not a "new Derived()"
            Derived newObject = new Derived();
            newObject.FromInt(Value);
            return newObject;
        }
    }
    

    【讨论】:

      【解决方案3】:

      原因

      Derived d = (int)3;
      

      不起作用是因为Derived 类型与调用此运算符所需的运算符Base 的返回值不完全匹配。请注意,您没有提供任何包含代码 new Derived(...) 的转换运算符,因此您不能以这种方式创建新的 Derived 实例也就不足为奇了。

      但请注意,相反的转换

      Derived v = ...;
      string s = (string)v;
      

      可以正常工作(就好像它是“继承的”,尽管由于static 关键字这并不是真正的继承)。

      【讨论】:

      • 我不认为有任何方法可以得到 OP 的期望行为?
      【解决方案4】:

      不,他们不是。线索如下:

      public **static** implicit operator Base(int Value)

      C# 中的静态方法只是可以访问私有类成员的全局函数。他们永远不会被继承

      【讨论】:

      • 虽然静态方法不被继承是正确的,但这与这个问题完全无关。 C# 3.0 规范的相关部分是第 6.4.3 节,其中我们描述了在哪些类型中搜索静态运算符:“此集合由源类型及其基类和目标类型及其基类组成”。
      • 哎呀。我会删除不正确的答案,但你的评论很值得保留,所以它必须留在那里让我看起来很糟糕:-)
      【解决方案5】:

      不,它不会那样工作。编译器不会隐式为您从基础向下转换为派生。基本上,你不能这样做......

      D d = new B();
      

      您将从字符串转换中获得基类实现,因为它会为您执行隐式向上转换。

      如果您不想将方法复制到具有整数扩展函数的派生类,例如(假设您的派生类称为 D)...

      public static class Ext
      {
          public static D IntAsD(this int val)
          {
              return new D(val);
          }
      }
      

      然后,你可以做你想做的......

      D d1 = 5.IntAsD();
      

      当然,它并不完美,但它可能符合您的需求。

      【讨论】:

        【解决方案6】:

        转换/运算符方法很方便,但正如其他人所指出的那样,它们并不总是会做你想做的事。特别是,如果编译器在 = 符号的两边都没有看到正确的类型,它甚至不会考虑您的转换/运算符。这就是为什么如果您执行以下操作会收到编译器警告:

        object str = "hello";
        if ( str == "hello" ) {
        }
        

        此外,使用 as/is 关键字时不考虑自定义转换。

        在考虑实现显式/隐式转换时,您还应该考虑实现 IConvertible 和/或实现 TypeConverter。当基类处理类型转换时,这为您提供了更大的灵活性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-03-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多