如何将某些对象转换为T的父类的泛型类?

时间:2009-12-14 08:28:21

标签: c# generics c#-4.0

这个问题的主要问题是当我们将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 {}

最后,我创建了一些函数来检索给定类T中的验证器。

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);   
}

当T为SimpleUserInfo时,此函数将正常工作,但当T为UserInfo时会出现问题。怎么解决这个问题?

PS。要解决此问题,不需要使用C#4.0的新功能。但我只是告诉你我将在C#4.0中应用这个解决方案。

谢谢,

3 个答案:

答案 0 :(得分:2)

我假设您正在寻找协方差,但该功能(C#4的新功能)仅适用于接口,而不适用于类。

编辑:根据您的编辑,您的意思是共同/逆转。但是,是否以及如何使接口IValidator<T>变为协变(这似乎是您正在寻找的内容)取决于IValidator<T>接口的方法和属性。

答案 1 :(得分:2)

我还没有安装.NET 4.0,所以我不确定正确使用协方差,但即使在使用Duck Typing库的.Net 2.0和3.5中也可以获得协方差支持(例如{{3} }或duck typing library by David Meyer

换句话说,GetValidator<T>中的最后一行应如下所示:

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

或(使用LinFu)

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

<强> [编辑]

我可能没有理解你的问题,但我相信它归结为:

您有一个通用接口,其通用参数类型为T:

IValidator<SimpleUserInfo> simpleUserValidator;

您希望将其转换为相同的通用接口,但使用通用参数,该参数是T的 base 类:

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 LinFu by Philip Laureano,但我还没有测试过它。这个例子适用于任何.Net版本,所以我把它包括在内是为了完整起见。

答案 2 :(得分: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