2009-06-09 21 views
7

No estoy seguro de lo que estoy haciendo mal aquí. Tengo una clase genérica, que es básicamente un número entero glorificado, con algunos métodos para cierta cadena de formato, así como en/de cuerda y int conversiones:¿Se heredan los métodos de conversión de implícita/explícita en C#?

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); 
    } 
} 

y funciona bien. Puedo utilizar con éxito las conversiones implícitas y explícitas:

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

Entonces se derivan de esta clase, diferentes clases hijas, que se convierten en diferentes bits de activación/desactivación nombrados en m_Value. Por ejemplo:

public class Derived : Base 
{ 

} 

Y entonces no puedo usar mi implícita a/desde int conversiones:

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

Incluso esto le da el mismo error:

Derived d = (int)3; 

son las conversiones implícitas/explícitos no heredado en la clase derivada? Esto requerirá una gran cantidad de copia de código si no es así.

RESPUESTA ¡Muchas gracias por las respuestas rápidas! Todos se merecen la marca de verificación 'respuesta', son todas muy buenas respuestas. La clave es pensar en los tipos en ambos lados del signo igual. Ahora que lo pienso así, tiene mucho sentido.

Obviamente, solo tengo que volver a escribir mis conversiones "en derivadas". Las conversiones "a Int32, cadena, etc." aún se aplican.

+1

Para más detalles sobre algunas de las sutilezas de cómo funciona todo esto, es posible que desee leer mi artículo sobre el tema: http://blogs.msdn.com/ericlippert/archive/2007/04/16 /chained-user-defined-explicit-conversions-in-c.aspx –

Respuesta

6

La razón

Derived d = (int)3; 

que no funciona es porque el tipo Derived no coincide exactamente con el valor devuelto por el operador Base como se requiere para invocar este operador. Tenga en cuenta que no ha proporcionado ningún operador de conversión que contenga el código new Derived(...), por lo que no es sorprendente que no pueda realizar nuevas instancias Derived de esta manera.

Nótese, sin embargo, que la conversión opuesta

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

funciona bien (como si estuviera "heredado", aunque esto no es realmente la herencia debido a la palabra clave static).

+1

Supongo que no hay forma de obtener el comportamiento deseado del OP. – weberc2

3

No, no funcionará de esa manera. El compilador no implícitamente downcast desde una base a un derivado para usted. Básicamente, no se puede hacer ...

D d = new B(); 

obtendrá sus implmentations clase base de yeso cadena, ya que hará el upcasting implícita para usted.

Usted podría hacer un trabajo alrededor si no desea copiar sus métodos a la clase derivada de una función de la extensión de los números enteros, como (asumiendo que su clase derivada se llama D) ...

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

a continuación, se puede hacer lo que quiera con ...

D d1 = 5.IntAsD(); 

Por supuesto, no es perfecto, pero podría adaptarse a sus necesidades.

2

Los operadores sobrecargados (incluidas las conversiones) se heredan, pero no de la manera que aparentemente espera. En particular, no se extienden mágicamente para operar en tipos derivados. Echemos un vistazo a su código de nuevo:

Derived d = 3; 

En este punto, el compilador tiene dos clases para trabajar, System.Int32, y derivado. Entonces busca operadores de conversión en esas clases. Se tendrá en cuenta los heredados por derivadas de la base, a saber:

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

El segundo no coincide porque el argumento debe ser un Int32. El primero coincide con el argumento, pero luego su tipo de devolución es Base, y ese no coincide. Por otro lado, puede escribir lo siguiente:

string s = (string) new Derived(); 

y funcionará, porque el operador explícita de la base existe y es aplicable (se espera que el argumento sea la base, hereda derivadas de la base y, por tanto, coincide con el tipo de argumento)

La razón por la cual el compilador no redefinirá automágicamente los operadores de conversión en los tipos derivados "correctamente" es porque no hay una forma genérica de hacerlo.

0

No, no lo son. Aquí está la clave:

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

métodos estáticos en C# son sólo funciones globales con acceso a miembros de la clase privados. Nunca se heredan

+3

Aunque es correcto que los métodos estáticos no se hereden, eso es completamente irrelevante para esta pregunta. La sección relevante de la especificación C# 3.0 es la sección 6.4.3, donde describimos qué tipos se buscan operadores estáticos: "Este conjunto consta del tipo de fuente y sus clases base y el tipo de destino y sus clases base". –

+1

oops. Eliminaría la respuesta incorrecta, pero vale la pena conservar tu comentario, así que tendrá que permanecer allí para hacerme quedar mal :-) –

1

Los métodos de conversión/operador son una conveniencia, pero como otros han señalado, no siempre van a hacer lo que usted desea. Particularmente, si el compilador no ve los tipos correctos en ambos lados del signo =, ni siquiera considerará su conversión/operador. Es por esto que si el siguiente se obtiene una advertencia del compilador:

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

Por otra parte, las conversiones personalizadas no se consideran cuando se utiliza el AS/es palabras clave.

Al pensar en implementar una conversión explícita/implícita, también debe considerar implementar IConvertible y/o implementar un TypeConverter. Esto le da mucha más flexibilidad al tener conversiones de tipo de manejo de clase base.

0

Aunque no puede hacer esto directamente, hay algunos atajos que puede usar para habilitar la reutilización de código.

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; 
    } 
} 
Cuestiones relacionadas