2012-04-25 2 views
5

я создаю универсальный конвертерНевозможно изменить тип для NULLABLE в родовом методе

Вот пример кода общего преобразователя

bool TryReaderParse<TType>(object data, out TType value) 
{ 
    value = default(TType); 
    Type returnType = typeof(TType); 
    object tmpValue = null; 

    if (returnType == typeof(DateTime)) 
    { 
     tmpValue = StringToDatetime(data.ToString()); 
    } 
    else if (returnType == typeof(DateTime?)) // THIS IF FIRES 
    { 
     tmpValue = StringToNullableDatetime(data.ToString()); 
    } 

    value = (TType)Convert.ChangeType(tmpValue, returnType); // THROWS 
} 

public DateTime? StringToNullableDatetime(string date) 
{ 
    DateTime? datetime = null; 
    if (!string.IsNullOrEmpty(date)) 
    { 
     datetime = DateTime.Parse(date, new CultureInfo(Resources.CurrentCulture)); 
    } 

    return datetime; 
} 

И это, как я использую его:

void foo() 
{ 
    DateTime? date = null; 
    TryReaderParse<DateTime?>("25/12/2012", out date); 
} 

Исключенное исключение говорит, что оно не может конвертировать из DateTime в Nullable<DateTime>. Поскольку метод создает и возвращает тип с нулевым значением, как происходит сбой кастинга?

В конце я хочу, чтобы в этом конкретном примере с нулевым значением DateTime.

редактировать Проблема заключается в том, что StringToNullableDatetime метод возвращает Datetime? и отливка говорит, что не может конвертировать из Datetime

Поскольку метод StringToNullableDatetime возвращает обнуляемого DateTime, как это возможно, что Convert.ChangeType не может видеть, что прошло аргумент является нулевым?

Ps. Я читал ответы, например, this, которые делают противоположное (отличное от нуля).

ответ

18

Исключенное исключение говорит, что оно не может конвертировать из DateTime в Nullable<DateTime>. Поскольку метод создает и возвращает тип с нулевым значением, как происходит сбой кастинга?

Хороший вопрос. Это не удается, потому что не существует такой вещи, как поле с нулевым значением. Когда вы конвертируете DateTime? в object, вы либо получаете нулевую ссылку, если DateTime? имеет значение NULL, либо вы получаете значение , a DateTime. Вы никогда не получите коробку с нулевым значением; такой вещи нет.

Поэтому в этом поле вы получите либо нулевой, либо действительный DateTime. Затем вы передаете Convert для преобразования этого значения в NULL DateTime, и Convert не знает, как это сделать.

Мой совет, что вы полностью отказываетесь от этой линии атаки; этот код является пограничным оскорбительным для дженериков. Каждый раз, когда вы включаете определенный тип общего кода, ваш код больше не generic, и вы, вероятно, ошибаетесь. Если вы хотите сделать «попробовать» -style метод DateTimes затем просто написать, что:

DateTime? TryReadDateTime(object data) 
{ 
    ... return null if the object cannot be read as a datetime ... 
} 

Написать такой метод каждый типа, что вы собираетесь читать. Пользователь предпочел бы написать:

DateTime? d = TryReadDateTime(data); 
if (d != null) ... 

Чем

DateTime d; 
bool b = TryRead<DateTime>(data, out d); 
if (b) ... 
0

documentation С, эта линия будет ошибка, если:

значение равно нулю и conversionType является типом значения

Nullable<T> является структурой и, следовательно, тип значения, таким образом, вы не можете используйте этот метод, если ваше значение равно null. Вы уже обрабатываете даты отдельно, поэтому зачем использовать ChangeType в таких случаях?

+0

отредактировал мой вопрос. Моя проблема в том, что я не могу вернуть Nullable datetime. Строка 'Convert.ChangeType' не может видеть, что переданный аргумент является нулевым. – Odys

0

То, как обмениваются nullables, generics и boxing, является странным. Вы можете быть лучше определения двух методов:

 
bool TryReaderParse(object data, out TType value); 
bool TryReaderParse(object data, out TType? value) where TType : struct; 

В рамках второго метода, ваш код может просто произвести TType и присвоить его TType? без труда.

+2

Во-первых, метод не может быть перегружен только по ограничениям. Во-вторых, если метод возвращает значение с нулевым значением, то * зачем ему также нужно возвращать bool *? Если вы это сделаете, то правильные подписи будут «T TryParseClass (данные объекта), где T: class' и' T? TryParseStruct (данные объекта), где T: struct'. Нет необходимости. –