¿Cómo proporciono soporte de yeso personalizado para mi clase?

102

¿Cómo proporciono apoyo para transmitir mi clase a otros tipos? Por ejemplo, si tengo mi propia implementación de administrar a byte[], y quiero permitir que las personas envíen mi clase a a byte[], que solo devolverá el miembro privado, ¿cómo haría esto?

¿Es una práctica común dejar que ellos también conviertan esto en una cadena, o debería simplemente anular ToString()(o ambos)?

esac
fuente

Respuestas:

112

Debería anular el operador de conversión, usando implicito explicitdependiendo de si desea que los usuarios tengan que transmitirlo o si desea que suceda automágicamente. Generalmente, una dirección siempre funcionará, ahí es donde se usa implicit, y la otra dirección a veces puede fallar, ahí es donde se usa explicit.

La sintaxis es así:

public static implicit operator dbInt64(Byte x)
{
    return new dbInt64(x);
}

o

public static explicit operator Int64(dbInt64 x)
{
    if (!x.defined)
        throw new DataValueNullException();
    return x.iVal;
}

Para su ejemplo, diga desde su Tipo personalizado ( MyType-> byte[]siempre funcionará):

public static implicit operator byte[] (MyType x)
{
    byte[] ba = // put code here to convert x into a byte[]
    return ba;
}

o

public static explicit operator MyType(byte[] x)
{
    if (!CanConvert)
        throw new DataValueNullException();

    // Factory to convert byte[] x into MyType
    MyType mt = MyType.Factory(x);
    return mt;
}
Charles Bretana
fuente
36

Puede declarar operadores de conversión en su clase utilizando las palabras clave explicito implicit.

Como regla general, solo debe proporcionar implicitoperadores de conversión cuando la conversión no pueda fallar. Utilice explicitoperadores de conversión cuando la conversión pueda fallar.

public class MyClass
{
    private byte[] _bytes;

    // change explicit to implicit depending on what you need
    public static explicit operator MyClass(byte[] b)
    {
        MyClass m = new MyClass();
        m._bytes = b;
        return m;
    }

    // change explicit to implicit depending on what you need
    public static explicit operator byte[](MyClass m)
    {
        return m._bytes;
    }
}

Usar explicitsignifica que los usuarios de su clase necesitarán realizar una conversión explícita:

byte[] foo = new byte[] { 1, 2, 3, 4, 5 };
// explicitly convert foo into an instance of MyClass...
MyClass bar = (MyClass)foo;
// explicitly convert bar into a new byte[] array...
byte[] baz = (byte[])bar;

Usar implicitsignifica que los usuarios de su clase no necesitan realizar una conversión explícita, todo sucede de manera transparente:

byte[] foo = new byte[] { 1, 2, 3, 4, 5 };
// imlpicitly convert foo into an instance of MyClass...
MyClass bar = foo;
// implicitly convert bar into a new byte[] array...
byte[] baz = bar;
LukeH
fuente
6

Prefiero tener algún método que lo haga en lugar de sobrecargar al operador de transmisión.

Consulte c # explícito e implícito, pero tenga en cuenta que de ese ejemplo, utilizando el método explícito, si lo hace:

string name = "Test";
Role role = (Role) name;

Entonces todo está bien; sin embargo, si usa:

object name = "Test";
Role role = (Role) name;

Ahora obtendrá una InvalidCastException porque la cadena no se puede convertir a Role, por qué, el compilador solo busca conversiones implícitas / explícitas en tiempo de compilación en función de su tipo compilado. En este caso, el compilador ve el nombre como un objeto en lugar de una cadena y, por lo tanto, no usa el operador sobrecargado de Role.

Chris Chilvers
fuente
Mirando el ejemplo al que vinculó, parece crear una nueva instancia del objeto en cada lanzamiento. ¿Alguna idea de cómo realizar operaciones de obtención / configuración en un miembro actual de la clase?
esac
3

Para el soporte de transmisión personalizado, debe proporcionar operadores de transmisión (explícitos o implícitos). El siguiente ejemplo de la clase EncodedString es una implementación simplista de cadena con codificación personalizada (puede ser útil si tiene que procesar cadenas enormes y tener problemas de consumo de memoria porque las cadenas .Net son Unicode, cada carácter ocupa 2 bytes de memoria) y EncodedString puede tomar 1 byte por carácter).

EncodedString se puede convertir a byte [] y System.String. Los comentarios en el código arrojan algo de luz y también explican un ejemplo en el que la conversión implícita puede ser peligrosa.

Por lo general, necesita una muy buena razón para declarar operadores de conversión en primer lugar porque.

Hay más lectura disponible en MSDN .

class Program
{
    class EncodedString
    {
        readonly byte[] _data;
        public readonly Encoding Encoding;

        public EncodedString(byte[] data, Encoding encoding)
        {
            _data = data;
            Encoding = encoding;
        }

        public static EncodedString FromString(string str, Encoding encoding)
        {
            return new EncodedString(encoding.GetBytes(str), encoding);
        }

        // Will make assumption about encoding - should be marked as explicit (in fact, I wouldn't recommend having this conversion at all!)
        public static explicit operator EncodedString(byte[] data)
        {
            return new EncodedString(data, Encoding.Default);
        }

        // Enough information for conversion - can make it implicit
        public static implicit operator byte[](EncodedString obj)
        {
            return obj._data;
        }

        // Strings in .Net are unicode so we make no assumptions here - implicit
        public static implicit operator EncodedString(string text)
        {
            var encoding = Encoding.Unicode;
            return new EncodedString(encoding.GetBytes(text), encoding);
        }

        // We have all the information for conversion here - implicit is OK
        public static implicit operator string(EncodedString obj)
        {
            return obj.Encoding.GetString(obj._data);
        }
    }

    static void Print(EncodedString format, params object[] args)
    {
        // Implicit conversion EncodedString --> string
        Console.WriteLine(format, args);
    }

    static void Main(string[] args)
    {
        // Text containing russian letters - needs care with Encoding!
        var text = "Привет, {0}!";

        // Implicit conversion string --> EncodedString
        Print(text, "world");

        // Create EncodedString from System.String but use UTF8 which takes 1 byte per char for simple English text
        var encodedStr = EncodedString.FromString(text, Encoding.UTF8);
        var fileName = Path.GetTempFileName();

        // Implicit conversion EncodedString --> byte[]
        File.WriteAllBytes(fileName, encodedStr);

        // Explicit conversion byte[] --> EncodedString
        // Prints *wrong* text because default encoding in conversion does not match actual encoding of the string
        // That's the reason I don't recommend to have this conversion!
        Print((EncodedString)File.ReadAllBytes(fileName), "StackOverflow.com");

        // Not a conversion at all. EncodingString is instantiated explicitly
        // Prints *correct* text because encoding is specified explicitly
        Print(new EncodedString(File.ReadAllBytes(fileName), Encoding.UTF8), "StackOverflow.com");

        Console.WriteLine("Press ENTER to finish");
        Console.ReadLine();
    }
}
Konstantin Spirin
fuente