web-dev-qa-db-ja.com

クラスにカスタムキャストサポートを提供するにはどうすればよいですか?

クラスを他の型にキャストするためのサポートを提供するにはどうすればよいですか?たとえば、_byte[]_を管理する独自の実装があり、プライベートクラスを返すだけのクラスを_byte[]_にキャストできるようにしたい場合、どうすればよいですか?

これを文字列にもキャストするのは一般的な習慣ですか、それともToString()(または両方)をオーバーライドするだけですか?

88
esac

implicit または explicit のいずれかを使用して、変換演算子をオーバーライドする必要があります。自動的に実行するかどうか。一般に、1つの方向は常に機能します。それはimplicitを使用する場所であり、他の方向は時々失敗することがあります、それはexplicitを使用する場所です。

構文は次のとおりです。

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

または

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

たとえば、カスタムタイプ(MyType-> byte[]は常に機能します):

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

または

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

explicit または implicit キーワードを使用して、クラスで変換演算子を宣言できます。

一般的な経験則として、implicit変換演算子は、変換が失敗する可能性がない場合にのみ提供する必要があります。変換が失敗する可能性がある場合は、explicit変換演算子を使用します。

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

explicitを使用すると、クラスのユーザーは明示的な変換を行う必要があります。

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;

implicitを使用することは、クラスのユーザーが明示的な変換を実行する必要がないことを意味し、すべて透過的に行われます。

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

カスタムキャストをサポートするには、キャスト演算子(明示的または暗黙的)を提供する必要があります。次のEncodedStringクラスの例は、カスタムエンコーディングを使用した文字列の単純な実装です(.Net文字列はUnicode-すべての文字が2バイトのメモリを使用するため、巨大な文字列を処理してメモリ消費の問題が発生する場合に役立ちます) EncodedStringは、1文字につき1バイトを取ることができます)。

EncodedStringはbyte []およびSystem.Stringに変換できます。コード内のコメントはいくつかの光を当て、また暗黙的な変換が危険な場合の例を説明します。

通常、最初に変換演算子を宣言するのには非常に正当な理由が必要です。

[〜#〜] 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

キャスト演算子をオーバーロードするのではなく、それを行うメソッドが必要です。

explicit and implicit c# を参照してください。ただし、その例では、明示的な方法を使用すると、次のようになります。

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

その後、すべてが正常です。ただし、次を使用する場合:

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

文字列をロールにキャストできないため、InvalidCastExceptionが発生します。これは、コンパイラーが、コンパイルされたタイプに基づいて、コンパイル時に暗黙的/明示的なキャストのみを探すためです。この場合、コンパイラは名前を文字列ではなくオブジェクトと見なし、したがって、Roleのオーバーロードされた演算子を使用しません。

3
Chris Chilvers