Come posso fornire supporto personalizzato per il cast per la mia classe?


103

Come posso fornire supporto per trasmettere la mia classe ad altri tipi? Ad esempio, se ho la mia implementazione della gestione di un filebyte[] e voglio che le persone trasmettano la mia classe a a byte[], che restituirà semplicemente il membro privato, come dovrei farlo?

È pratica comune lasciare che anche loro trasmettano questo su una stringa, o dovrei semplicemente sovrascrivere ToString()(o entrambi)?

Risposte:


113

Dovresti sovrascrivere l'operatore di conversione, utilizzando implicito a explicitseconda se desideri che gli utenti debbano trasmetterlo o se desideri che avvenga automaticamente. In generale, una direzione funzionerà sempre, è lì che usi implicit, e l'altra direzione a volte può fallire, è lì che usiexplicit .

La sintassi è così:

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

Per il tuo esempio, dì dal tuo tipo personalizzato ( MyType-> byte[]funzionerà sempre):

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

36

Puoi dichiarare gli operatori di conversione sulla tua classe usando explicitoimplicit parole chiave .

Come regola generale, dovresti fornire implicitoperatori di conversione solo quando la conversione non può fallire. Utilizza gli explicitoperatori di conversione quando la conversione potrebbe non riuscire.

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

Usare explicitsignifica che gli utenti della tua classe dovranno eseguire una conversione esplicita:

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;

Usare implicitsignifica che gli utenti della tua classe non hanno bisogno di eseguire una conversione esplicita, tutto avviene in modo trasparente:

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;

6

Preferisco avere un metodo che lo faccia piuttosto che sovraccaricare l'operatore cast.

Vedi esplicito e implicito c # ma nota che da quell'esempio, usando il metodo esplicito, se lo fai:

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

Allora va tutto bene; tuttavia, se utilizzi:

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

Ora otterrai un'eccezione InvalidCastException perché non è possibile eseguire il cast della stringa in Role, perché, il compilatore cerca solo cast impliciti / espliciti in fase di compilazione in base al tipo compilato. In questo caso il compilatore vede il nome come un oggetto piuttosto che come una stringa, e quindi non usa l'operatore di overload di Role.


Guardando l'esempio a cui ti sei collegato, sembra creare una nuova istanza dell'oggetto su ogni cast. Qualche idea su come eseguire semplicemente il tipo di operazioni get / set su un membro corrente della classe?
esac

3

Per il supporto cast personalizzato è necessario fornire operatori cast (espliciti o impliciti). Il seguente esempio di classe EncodedString è un'implementazione semplicistica della stringa con codifica personalizzata (può essere utile se devi elaborare stringhe enormi e incorrere in problemi di consumo di memoria perché le stringhe .Net sono Unicode - ogni carattere richiede 2 byte di memoria - e EncodedString può richiedere 1 byte per carattere).

EncodedString può essere convertito in byte [] e in System.String. I commenti nel codice fanno luce e spiegano anche un esempio in cui la conversione implicita può essere pericolosa.

Di solito hai bisogno di un'ottima ragione per dichiarare gli operatori di conversione in primo luogo perché.

Ulteriori letture sono disponibili su 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();
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.