How do I provide custom cast support for my class?

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

通常的做法是让它们也将此转换为字符串,还是应该只覆盖 ToString()(或两者兼而有之) ?

63077 次浏览

我更喜欢使用一些方法来完成这项工作,而不是重载强制转换操作符。

参见 显式和隐式 c # ,但请注意,从该示例中,如果使用显式方法,则:

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

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

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

现在您将得到一个 InvalidCastException,因为字符串不能被强制转换为 Role,为什么,编译器只在编译时根据它们的编译类型查找隐式/显式强制转换。在这种情况下,编译器将 name 视为对象而不是字符串,因此不使用 Role 的重载运算符。

您需要覆盖转换操作符,使用 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;
}

例如,假设从自定义 Type (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
return MyType.Factory(x);
}

可以使用 explicitimplicit关键字在类上声明转换运算符。

As a general rule-of-thumb, you should only provide implicit conversion operators when the conversion can't possibly fail. Use explicit conversion operators when the conversion might fail.

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;

Using implicit means that users of your class don't need to perform an explicit conversion, it all happens transparently:

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;

对于自定义强制转换支持,需要提供强制转换运算符(显式或隐式)。 下面的 EncodedString 类示例是使用自定义编码的字符串的简单实现(如果必须处理巨大的字符串并遇到内存消耗问题,可能会很有用,因为。Net 字符串是 Unicode-每个字符占用2个字节的内存-EncodedString 每个字符占用1个字节)。

EncodedString 可以转换为 byte []和 System.String。 Comments in code shed some light and also explain an example when implicit conversion can be dangerous.

通常您首先需要一个非常好的理由来声明任何转换运算符,因为。

进一步的读数可在 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();
}
}