如何为我的班级提供自定义演员表支持?


102

如何为将课程转换为其他类型提供支持?例如,如果我有自己的实现管理a byte[],并且我想让人们将我的班级强制转换为a byte[],而该类只会返回私有成员,那么我该怎么做?

让他们也将其也转换为字符串是一种常见的做法,还是我应该重写ToString()(或同时重写)?

Answers:


112

您需要使用implicitexplicit取决于您是否希望用户强制转换它,或者是否希望它自动发生,从而覆盖转换运算符。通常,一个方向将始终有效,这是您使用的地方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;
}

36

您可以使用explicitor 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;

6

我更喜欢有某种方法可以做到这一点,而不是重载强制转换运算符。

请参阅显式和隐式c#,但是请注意,从该示例开始,使用显式方法:

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

然后一切都很好;但是,如果您使用:

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

现在,您将获得InvalidCastException,因为无法将字符串强制转换为Role,这是为什么编译器仅在编译时根据其编译类型查找隐式/显式强制转换。在这种情况下,编译器将名称视为对象而不是字符串,因此不使用Role的重载运算符。


看您链接到的示例,似乎在每次转换时都创建该对象的新实例。知道如何在类的当前成员上获取/设置操作类型吗?
esac

3

对于自定义强制转换支持,您需要提供强制转换操作符(显式或隐式)。下面的EncodedString类示例是具有自定义编码的字符串的简化实现(如果您必须处理巨大的字符串并遇到内存消耗问题,这可能会很有用,因为.Net字符串是Unicode-每个字符占用2个字节的内存-并且EncodedString可以每个字符占用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();
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.