web-dev-qa-db-fra.com

Comment puis-je fournir un support de distribution personnalisé pour ma classe?

Comment puis-je fournir un support pour caster ma classe sur d'autres types? Par exemple, si j'ai ma propre implémentation de gestion d'un byte[], Et que je veux permettre aux gens de convertir ma classe en un byte[], Qui renverra simplement le membre privé, comment pourrais-je procéder ?

Est-il courant de les laisser également transtyper en chaîne, ou devrais-je simplement remplacer ToString() (ou les deux)?

88
esac

Vous devrez remplacer l'opérateur de conversion, en utilisant implicit ou explicit selon que vous souhaitez que les utilisateurs aient à le caster ou si vous voulez que cela se produise automatiquement. Généralement, une direction fonctionnera toujours, c'est là que vous utilisez implicit, et l'autre direction peut parfois échouer, c'est là que vous utilisez explicit.

La syntaxe est la suivante:

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

ou

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

Pour votre exemple, dites à partir de votre type personnalisé (MyType -> byte[] fonctionnera toujours):

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

ou

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;
}
99
Charles Bretana

Vous pouvez déclarer des opérateurs de conversion sur votre classe à l'aide des mots clés explicit ou implicit .

En règle générale, vous ne devez fournir des opérateurs de conversion implicit que lorsque la conversion ne peut pas échouer. Utilisez les opérateurs de conversion explicit lorsque la conversion peut échouer.

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

L'utilisation de explicit signifie que les utilisateurs de votre classe devront effectuer une conversion explicite:

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;

L'utilisation de implicit signifie que les utilisateurs de votre classe n'ont pas besoin d'effectuer une conversion explicite, tout se passe de manière 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;
30
LukeH

Pour la prise en charge des cast personnalisés, vous devez fournir des opérateurs de cast (explicites ou implicites). L'exemple suivant de la classe EncodedString est une implémentation simpliste de chaîne avec un codage personnalisé (peut être utile si vous devez traiter des chaînes énormes à énormes et rencontrer des problèmes de consommation de mémoire car les chaînes .Net sont Unicode - chaque caractère prend 2 octets de mémoire - et EncodedString peut prendre 1 octet par caractère).

EncodedString peut être converti en octet [] et en System.String. Les commentaires dans le code apportent un éclairage et expliquent également un exemple lorsque la conversion implicite peut être dangereuse.

Habituellement, vous avez besoin d'une très bonne raison pour déclarer tout opérateur de conversion en premier lieu, car.

De plus amples informations sont disponibles sur 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();
    }
}
3
Konstantin Spirin

Je préfère avoir une méthode qui fera cela plutôt que de surcharger l'opérateur de cast.

Voir c # explicite et implicite mais notez qu'à partir de cet exemple, en utilisant la méthode explicite, si vous faites:

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

Alors tout va bien; cependant, si vous utilisez:

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

Vous obtiendrez maintenant une InvalidCastException car la chaîne ne peut pas être convertie en rôle, pourquoi, le compilateur ne recherche que les transtypages implicites/explicites au moment de la compilation en fonction de leur type compilé. Dans ce cas, le compilateur voit le nom comme un objet plutôt que comme une chaîne et n'utilise donc pas l'opérateur surchargé de Role.

3
Chris Chilvers