2009-12-14 2 views
2

Основная проблема этого вопроса заключается в том, что мы передаем T в некоторую функцию и используем ее для создания какого-либо объекта, например, следующего утверждения.Как передать некоторый объект в общий класс родительского элемента T?

void SomeFunction<T> (object model) 
{ 
    (SomeClass<T>)model; 
} 

Все работает нормально. Но я хочу, чтобы объект модели был привязан к универсальному объекту класса, который является родителем T или великим родителем T, зависит от того, что не пусто. Как это сделать?

Обновлено # 1

Для большего понимания, пожалуйста, смотрите на следующем примере.

public partial class SimpleUserInfo 
{ 
    public string LogOnName { get;set; } 
    public string HashedPassword { get;set; } 
} 

public partial class UserInfo : SimpleUserInfo 
{ 
    pubic string Address { get;set; } 
} 

После создания некоторых моделей данных. Я создаю некоторый общий класс, который использует параметр UserInfo в качестве параметра.

public class SimpleUserInfoValidator : IValidator<SimpleUserInfo> 
{ 
    // logic to validate simple user info instance 
} 

И вот, я добавляю атрибут класса SimpleUserInfo.

[Validator(typeof(SimpleUserInfoValidator))] 
public partial class SimpleUserInfo {} 

Наконец, я создаю некоторые функции для извлечения валидаторов в данном классе Т.

public GetValidator<T>() 
{ 
    var attribute = (ValidatorAttribute)Attribute.GetCustomAttribute(type, typeof(ValidatorAttribute)); 

    if (attribute == null || attribute.ValidatorType == null) 
     return null; 

    var (IValidator<T>)Activator.CreateInstance(attribute.ValidatorType); 
} 

Эта функция отлично работает, когда Т SimpleUserInfo, но проблема возникает, когда Т UserInfo. Как это решить?

PS. Для решения этого вопроса не требуется использовать новую функцию C# 4.0. Но я просто расскажу вам о том, как применить это решение в C# 4.0.

Спасибо,

+0

Вы говорите, что проблема возникает, но вы не говорите, в чем проблема. Если вы передадите UserInfo для T, то что вы ожидаете? Вы не определили валидатор для UserInfo, поэтому он должен вернуть значение null. Не так ли? В чем проблема? –

+0

Я полагаю, что проблема связана с ковариацией - 'GetCustomAttribute' возвращает экземпляр' IValidator 'для' UserInfo', потому что ** базовый класс ** помечен этим атрибутом. Таким образом, 'GetValidator ' будет вызывать 'InvalidCastException', потому что параметры типового типа не совпадают. – Groo

+0

Я вижу вашу точку зрения. В этом случае вам понадобится * контравариантность *, а не * ковариация *. –

ответ

2

Я еще не установил .NET 4.0, поэтому я не уверен в правильности использования ковариации, но вы можете получить поддержку ковариации даже в .Net 2.0 и 3.5, используя библиотеку Duck Typing (например,duck typing library by David Meyer или LinFu by Philip Laureano:

Другими словами, последняя строка в GetValidator<T> должен выглядеть следующим образом:

// http://www.deftflux.net/blog/page/Duck-Typing-Project.aspx 
return DuckTyping.Cast<IValidator<T>> 
    Activator.CreateInstance(attribute.ValidatorType); 

или (используя Линьфу)

// http://www.codeproject.com/KB/cs/LinFuPart2.aspx 
DynamicObject dynamicObj = new DynamicObject 
    (Activator.CreateInstance(attribute.ValidatorType)); 
return dynamicObj.CreateDuck<IValidator<T>>() 

[Редактировать]

It возможно, что я не понял ваш вопрос, но я считаю, что это сводится к следующему:

У вас есть общий интерфейс с родовым параметром типа T:

IValidator<SimpleUserInfo> simpleUserValidator; 

Вы хотите бросить его в тот же общий интерфейс, но с общим параметром, который является базы класса T:

IValidator<SimpleUserInfo> ---> IValidator<UserInfo> 

Простое литье не будет работать, так как общий тип ковариации не поддерживается (по крайней мере, не в старых версиях .Net):

// this will throw an invalid cast exception 
IValidator<UserInfo> userValidator = (IValidator<UserInfo>) simpleUserValidator; 

Но это будет работать, используя утку набрав:

IValidator<UserInfo> userValidator = 
    DuckTyping.Cast<IValidator<UserInfo>> (simpleUserValidator); 

Еще раз, .Net 4.0 should handle covariance, но я не проверял. Этот пример будет работать в любой версии .Net, поэтому я включил его для полноты.

+0

Я думаю, вы пропустите понять мой вопрос. Основная проблема моего вопроса заключается в том, как создать новый объект, являющийся родителем T. Пожалуйста, прочитайте мой обновленный вопрос № 1. –

+0

Я думаю, что ваш обновленный ответ настолько ясен для решения проблемы и принятия приемлемого ответа. Кстати, в моей реальной проблеме я использую другой путь к этому вопросу, который имеет меньшую сложность, чем этот ответ. –

2

Я предполагаю, что вы ищете ковариации, но эта функция (новый C# 4) доступна только для интерфейсов, а не классы.

Редактировать: В соответствии с вашими изменениями вы подразумеваете co-/contravariance. Однако, если и как интерфейс IValidator<T> может быть сделан ковариантным (что похоже на то, что вы ищете) зависит от методов и свойств интерфейса IValidator<T>.

0

Это звучит, как вам нужно, чтобы ограничить тип T в какой-то интерфейс, который вы создаете:

void SomeFunction<T> (object model) where T : IHasParent 
{ 
    var parent = ((T)model).GetParent(); 
} 

public interface IHasParent 
{ 
    object GetParent(); 
} 

Это называется generic constraint.

Я думаю, вы должны быть в состоянии сделать это тоже:

void SomeFunction<T> (T model) where T : IHasParent 
{ 
    var parent = model.GetParent(); 
} 

public interface IHasParent 
{ 
    IHasParent GetParent(); 
} 

Я надеюсь, что это дает вам некоторые идеи взять вперед, дайте нам знать, что работает для вас.

Вы можете обрабатывать нули множеством способов, я оставлю это как упражнение для вас. Попробуйте найти, например, Null Coalescing Operator.

+0

Это не моя мысль. Пожалуйста, прочтите мой обновленный вопрос. –

+0

Я считаю, что ОП не означает «родительский класс», а скорее «базовый класс» в иерархии наследования. – Groo