Wie biete ich benutzerdefinierte Cast-Unterstützung für meine Klasse an?

101

Wie unterstütze ich das Casting meiner Klasse auf andere Typen? Wenn ich zum Beispiel meine eigene Implementierung der Verwaltung von a habe byte[]und die Leute meine Klasse in a umwandeln möchten byte[], wodurch nur das private Mitglied zurückgegeben wird, wie würde ich das tun?

Ist es üblich, dass sie dies auch in eine Zeichenfolge umwandeln, oder sollte ich einfach überschreiben ToString()(oder beides)?

esac
quelle

Antworten:

112

Sie müssten den Konvertierungsoperator überschreiben, indem Sie entweder implicitoder explicitabhängig davon verwenden, ob Benutzer ihn umwandeln müssen oder ob er automatisch ausgeführt werden soll. Im Allgemeinen funktioniert immer eine Richtung, in der Sie sie verwenden implicit, und die andere Richtung kann manchmal fehlschlagen. Dort verwenden Sie sieexplicit .

Die Syntax lautet wie folgt:

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

oder

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

Sagen Sie für Ihr Beispiel aus Ihrem benutzerdefinierten Typ ( MyType-> byte[]funktioniert immer):

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

oder

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
quelle
36

Sie können Konvertierungsoperatoren für Ihre Klasse entweder mit explicitoder deklarierenimplicit Schlüsselwörtern .

Als allgemeine Faustregel sollten Sie implicitKonvertierungsoperatoren nur angeben , wenn die Konvertierung möglicherweise nicht fehlschlagen kann. Verwenden Sie explicitKonvertierungsoperatoren, wenn die Konvertierung möglicherweise fehlschlägt.

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

Verwenden explicitbedeutet, dass Benutzer Ihrer Klasse eine explizite Konvertierung durchführen müssen:

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;

Verwenden implicitbedeutet, dass Benutzer Ihrer Klasse keine explizite Konvertierung durchführen müssen. Dies geschieht alles transparent:

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
quelle
6

Ich bevorzuge eine Methode, die dies ermöglicht, anstatt den Cast-Operator zu überladen.

Siehe explizites und implizites c # , beachten Sie jedoch, dass Sie in diesem Beispiel die explizite Methode verwenden, wenn Sie Folgendes tun:

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

Dann ist alles in Ordnung; Wenn Sie jedoch Folgendes verwenden:

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

Sie erhalten jetzt eine InvalidCastException, da die Zeichenfolge nicht in Role umgewandelt werden kann. Daher sucht der Compiler zur Kompilierungszeit nur nach impliziten / expliziten Casts basierend auf ihrem kompilierten Typ. In diesem Fall sieht der Compiler den Namen eher als Objekt als als Zeichenfolge und verwendet daher nicht den überladenen Operator von Role.

Chris Chilvers
quelle
Wenn Sie sich das Beispiel ansehen, mit dem Sie verlinkt haben, scheint es bei jeder Besetzung eine neue Instanz des Objekts zu erstellen. Haben Sie eine Idee, wie Sie Operationen für ein aktuelles Mitglied der Klasse abrufen / festlegen können?
Esac
3

Für die Unterstützung von benutzerdefinierten Besetzungen müssen Sie Besetzungsoperatoren (explizit oder implizit) bereitstellen. Das folgende Beispiel für die EncodedString-Klasse ist eine vereinfachte Implementierung von Zeichenfolgen mit benutzerdefinierter Codierung (kann nützlich sein, wenn Sie sehr große Zeichenfolgen verarbeiten und Probleme mit dem Speicherverbrauch haben müssen, da .Net-Zeichenfolgen Unicode sind - jedes Zeichen benötigt 2 Byte Speicher - und EncodedString kann 1 Byte pro Zeichen benötigen.

EncodedString kann in Byte [] und in System.String konvertiert werden. Kommentare im Code geben Aufschluss über ein Beispiel, in dem implizite Konvertierungen gefährlich sein können.

Normalerweise brauchen Sie einen sehr guten Grund, um überhaupt Konvertierungsoperatoren zu deklarieren, weil.

Weitere Informationen finden Sie auf 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
quelle