Sınıfım için özel döküm desteğini nasıl sağlayabilirim?


103

Sınıfımı diğer türlere yayınlamak için nasıl destek sağlayabilirim? Örneğin, kendi a yönetim uygulamasına sahipsem byte[]ve insanların sınıfımı a'ya atmasına izin vermek istersem byte[], bu sadece özel üyeyi geri döndürecektir, bunu nasıl yaparım?

Bunu da bir dizgeye çevirmelerine izin vermek yaygın bir uygulama mı yoksa sadece geçersiz kılmalı mıyım ToString()(veya her ikisini birden) mi?

Yanıtlar:


114

Kullanıcıların onu yayınlamasını isteyip istemediğinize implicitveya otomatik explicitolarak gerçekleşmesini isteyip istemediğinize bağlı olarak dönüştürme operatörünü geçersiz kılmanız gerekir. Genel olarak, bir yön her zaman işe yarar implicit, kullandığınız yer orasıdır ve diğer yön bazen başarısız olabilir, kullandığınız yer orasıdır explicit.

Sözdizimi şu şekildedir:

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

veya

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

Örneğiniz için, özel Türünüzden söyleyin ( MyType-> byte[]her zaman işe yarar):

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

veya

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

explicitVeya implicitanahtar sözcüklerini kullanarak sınıfınızda dönüştürme işleçleri bildirebilirsiniz .

Genel bir kural olarak, implicitdönüştürme işleçlerini yalnızca dönüştürme başarısız olamadığında sağlamalısınız . explicitDönüştürme başarısız olduğunda dönüştürme işleçlerini kullanın .

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

Kullanmak explicit, sınıfınızdaki kullanıcıların açık bir dönüşüm yapması gerektiği anlamına gelir:

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;

Kullanmak implicit, sınıfınızdaki kullanıcıların açık bir dönüşüm gerçekleştirmesine gerek olmadığı anlamına gelir, her şey şeffaf bir şekilde gerçekleşir:

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

Cast operatörünü aşırı yüklemek yerine bunu yapacak bir yönteme sahip olmayı tercih ederim.

Açık ve örtük c # bakın , ancak bunu yaparsanız, açık yöntemi kullanarak bu örnekten şunu unutmayın:

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

O zaman her şey yolunda; ancak, kullanıyorsanız:

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

Artık bir InvalidCastException alacaksınız çünkü dizge Role'a dönüştürülemiyor, neden, derleyici yalnızca derlenmiş türlerine göre derleme zamanında örtük / açık yayınlar arar. Bu durumda derleyici, adı dizeden ziyade bir nesne olarak görür ve bu nedenle Rolün aşırı yüklenmiş operatörünü kullanmaz.


Bağlandığınız örneğe bakıldığında, her dökümde nesnenin yeni bir örneğini oluşturuyor gibi görünüyor. Sınıfın mevcut bir üyesi üzerinde işlemlerin nasıl alınacağı / ayarlanacağı hakkında bir fikriniz var mı?
esac

3

Özel atama desteği için, dönüştürme işleçleri sağlamanız gerekir (açık veya örtük). Aşağıdaki EncodedString sınıfı örneği, özel kodlamalı dizenin basit bir uygulamasıdır (çok büyük dizeleri işlemeniz ve bellek tüketimi sorunlarıyla karşılaşmanız gerekiyorsa yararlı olabilir çünkü .Net dizeleri Unicode'dur - her karakter 2 bayt bellek alır - ve EncodedString karakter başına 1 bayt alabilir).

EncodedString, bayta [] ve System.String'e dönüştürülebilir. Koddaki yorumlar biraz ışık tutuyor ve ayrıca örtük dönüşümün tehlikeli olabileceği bir örneği açıklıyor.

Genellikle ilk etapta herhangi bir dönüştürme operatörünü bildirmek için çok iyi bir nedene ihtiyacınız vardır, çünkü.

Daha fazla bilgi MSDN'de mevcuttur .

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();
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.