2009-09-10 11 views
57

Jak zapewnić obsługę przesyłania mojej klasy do innych typów? Na przykład, jeśli mam własną implementację zarządzania byte[] i chcę, aby ludzie rzucili moją klasę na numer byte[], który zwróci tylko prywatnego członka, w jaki sposób mogę to zrobić?Jak zapewnić niestandardową obsadę dla mojej klasy?

Czy powszechną praktyką jest, aby pozwolić je również rzucić na ciąg znaków, czy też powinienem zastąpić ToString() (lub oba)?

Odpowiedz

64

Konieczne będzie przesłonięcie operatora konwersji przy użyciu opcji implicit lub explicit w zależności od tego, czy użytkownicy mają ją przesyłać, czy też mają ją uruchamiać w sposób automatyczny. Ogólnie rzecz biorąc, jeden kierunek zawsze działa, to jest miejsce, w którym używasz implicit, a inny kierunek może czasem zawieść, to tam używasz explicit.

Składnia jest tak:

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

lub

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

Dla przykładu, powiedzmy ze swojego niestandardowego typu (MyType ->byte[] zawsze działa):

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

lub

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

Dzięki, dokładnie to, co chciałem zrobić. – esac

2

Wolę mieć metodę, która to zrobi zamiast przeciążać operatora obsady.

Zobacz explicit and implicit c# jednak pamiętać, że od tego przykładu, stosując wyraźny sposób, jeśli nie:

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

wtedy wszystko jest w porządku; Jednakże, jeśli używasz:

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

Będziesz teraz uzyskać InvalidCastException ponieważ łańcuch nie może być oddane do naśladowania, dlaczego kompilator patrzy tylko dla ukrytych/wyraźnych rzutów w czasie kompilacji na podstawie ich skompilowanego typu. W tym przypadku kompilator widzi nazwę jako obiekt, a nie łańcuch, a zatem nie używa przeciążonego operatora Role.

+0

Patrząc na przykład, z którym się łączyłeś, wydaje się, że tworzy nowe wystąpienie obiektu na każdym rzucie. Każdy pomysł, jak po prostu uzyskać/ustawić typ operacji na bieżącym przedstawicielu klasy? – esac

17

Możesz zadeklarować operatorów konwersji w swojej klasie, używając słów kluczowych explicit lub implicit.

Jako ogólną zasadę należy podać tylko operatorów konwersji implicit, gdy konwersja nie może zawieść. Użyj operatorów konwersji explicit, gdy konwersja może się nie udać.

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

Korzystanie explicit oznacza, że ​​użytkownicy klasie będzie musiał zrobić wyraźną przemianę:

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; 

Korzystanie implicit oznacza, że ​​użytkownicy z twojej klasy nie muszą wykonywać wyraźne konwersji, to wszystko dzieje się to w sposób przejrzysty:

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

W przypadku niestandardowego obsady obsady należy podać operatorów odlewania (jawne lub niejawne). Następujący przykład klasy EncodedString to uproszczona implementacja łańcucha znaków z niestandardowym kodowaniem (może być przydatna, jeśli musisz przetwarzać olbrzymie ciągi znaków i uruchamiać problemy związane z pamięcią, ponieważ ciągi .Net to kod Unicode - każda postać zajmuje 2 bajty pamięci - i EncodedString może zająć 1 bajt na znak).

EncodedString można przekonwertować na byte [] i na System.String. Komentarze w kodzie rzucają trochę światła, a także wyjaśniają przykład, kiedy niejawna konwersja może być niebezpieczna.

Zwykle potrzebujesz bardzo dobrego powodu do zadeklarowania jakichkolwiek operatorów konwersji, ponieważ.

Dalsze czytanie jest dostępne pod adresem 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(); 
    } 
} 
Powiązane problemy