Как напрямую напечатать разбросанную структуру в C#?
-
10-10-2019 - |
Вопрос
У меня есть пространство имен структур, которые представляют различные единицы измерения (метры, ноги, дюймы и т. Д.) ... в общей сложности 12, сгенерированные любезность шаблонов T4 :).
Каждая структура несет неявные операторы литья для поддержки лишения значения любому другому типу значения измерения, поэтому следующий Sytax является законным:
var oneThousandMeters = new Meters(1000);
Kilometers aKilo = oneThousandMeters ; // implicit cast OK. Value = 1 Km
Чтобы добавить к радости, есть все уловки класса под названием Расстояние который может содержать любую единицу измерения, а также может быть неявно поднять на и измерение ...
var magnum = new Distance(12, DistanceUnits.Inches);
Feet wifesDelight = magnum; // implicit cast OK. Value = 1 foot.
Следуя стандарту .NET Framework, все форматирование и анализ строк обрабатываются внешним форматом, который реализует IcustomFormatter. К сожалению, это означает, что значение штуковывается, когда оно передается методу формата, и метод формата должен проверить объект на каждом известном типе измерения, прежде чем он сможет действовать на него. Внутренне метод формата в любом случае просто отражает измерение до значения расстояния, так что вот вопрос ....
Вопрос:
public string Format(string format, object arg, IFormatProvider formatProvider)
{
Distance distance;
// The following line is desired, but fails if arg != typeof(Distance)
distance = (Distance)arg;
// But the following tedious code works:
if(arg is Distance)
distance = (Distance)arg;
else if(arg is Meters)
distance = (Distance)(Meters)arg; // OK. compile uses implicit cast.
else if(arg is Feet)
distance = (Distance)(Feet)arg; // OK. compile uses implicit cast.
else if(arg is Inches)
distance = (Distance)(Inches)arg; // OK. compile uses implicit cast.
else
... // tear you hair out for all 12 measurement types
}
Есть ли какие -либо решения для этого, или это только один из тех неразрешимых недостатков типов стоимости?
PS: я проверил эта почта, и хотя вопрос похож, это не то, что я ищу.
Решение
Что ж, это вопрос отделения переоборудования распаковки от определенного пользователя преобразования. Вы хотите, чтобы оба произошли - и вы должны указать тип в Unbox, а также сообщить компилятору, когда вы хотите определить пользователь преобразование. Пользовательский преобразование должно быть выбрано в компиляция Время, если вы не используете динамическое печатание, поэтому компилятор должен знать, из какого типа он пытается преобразовать.
Один вариант - иметь IDistance
интерфейс которые все внедряют структуры. Тогда вы могли бы просто использовать:
IDistance distanceArg = arg as IDistance;
if (distanceArg != null)
{
Distance distance = distanceArg.ToDistance();
}
Поскольку у вас уже есть значение в штучной упаковке, использование интерфейса не вызовет дополнительное бокс или что -то в этом роде. Каждый ToDistance
Реализация, вероятно, может просто использовать неявное преобразование:
public Distance ToDistance()
{
return this;
}
... или вы можете использовать конверсию ToDistance
.
Другие советы
Да, это только одна из тех вещей, с которой вы должны жить.
Вы сталкиваетесь с одним и тем же, если вы вкладываете целое число в объект:
int a = 0;
object b = a;
int c = (int)b; // this works
short d = (short)b; // this fails
short e = (short)(int)b; // this works