Как я могу предоставить пользовательскую поддержку для моего класса?
Как мне предоставить поддержку для добавления моего класса в другие типы? Например, если у меня есть собственная реализация управления byte[]
, и я хочу, чтобы люди бросали мой класс в byte[]
, который просто вернет частный член, как бы я это сделал?
Это общепринятая практика, чтобы позволить им также передать это строке, или я должен просто переопределить ToString()
(или оба)?
Ответы
Ответ 1
Вам нужно будет переопределить оператор преобразования, используя implicit
или explicit
в зависимости от того, хотите ли вы, чтобы пользователи должны были его использовать или хотите, чтобы это произошло автоматически. Как правило, одно направление всегда будет работать, когда вы используете 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;
}
Ответ 2
Вы можете объявлять операторы преобразования в своем классе, используя 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;
Ответ 3
Я предпочитаю иметь какой-то метод, который будет делать это, а не перегружать оператор трансляции.
Смотрите явный и неявный С#, но обратите внимание, что из этого примера, используя явный метод, выполните следующие действия:
string name = "Test";
Role role = (Role) name;
Тогда все в порядке; однако, если вы используете:
object name = "Test";
Role role = (Role) name;
Теперь вы получите InvalidCastException, потому что строка не может быть передана роли, поэтому компилятор ищет неявные/явные приведения во время компиляции на основе их скомпилированного типа. В этом случае компилятор видит имя как объект, а не строку и, следовательно, не использует оператор с перегрузкой Role.
Ответ 4
Для поддержки произвольной поддержки вам необходимо предоставить операторы литья (явные или неявные).
Следующий пример класса EncodedString - упрощенная реализация строки с пользовательской кодировкой (может быть полезна, если вам приходится обрабатывать огромные огромные строки и запускать проблемы с потреблением памяти, потому что строки .Net - это Unicode - каждый символ занимает 2 байта памяти - и EncodedString может принимать 1 байт за char).
EncodedString может быть преобразован в байт [] и в 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 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();
}
}