类型受限的C#泛型无效强制转换

类型受限的C#泛型无效强制转换,c#,.net,generics,C#,.net,Generics,针对.net 4.0,我尝试构建以下类: public class ConfigurationElementCollection<TElement, TParent> where TElement : ParentedConfigElement<TParent>, new() where TParent : class { public TParent ParentElement { get; set; } protected Con

针对.net 4.0,我尝试构建以下类:

public class ConfigurationElementCollection<TElement, TParent> 
    where TElement : ParentedConfigElement<TParent>, new() 
    where TParent : class
{
    public TParent ParentElement { get; set; }

    protected ConfigurationElement CreateNewElement()
    {
        //**************************************************
        //COMPILER GIVES TYPE CONVERSION ERROR ON THIS LINE!
        //**************************************************
        return new TElement { ParentCollection = this };
    }
}

public class ParentedConfigElement<TParent> : ConfigurationElement where TParent : class
{
    internal ConfigurationElementCollection<ParentedConfigElement<TParent>, TParent> 
        ParentCollection { get; set; }

    protected TParent Parent
    {
        get
        {
            return ParentCollection != null ? ParentCollection.ParentElement : null;
        }
    }
}
不幸的是,我得到了同样的编译器错误。为什么会这样?我做错了什么?如果不使用
动态
类型,我能做些什么来解决这个问题呢?

基于

您将需要一个具有此签名的构造函数,设置
ParentCollection
属性


您也可以尝试使用反射。看看这个答案,你的问题是你有一个类型
CEC
,你试图将它分配给
CEC
类型的属性,这是你不能做的,就像
List
不能分配给类型
List
的存储一样,即使
string
派生自
object

不使用隐式运算符或动态的干净解决方案是使用接口:

public interface IConfigurationElementCollection<TParentedConfig, TParent> 
    where TParentedConfig : ParentedConfigElement<TParent>
    where TParent : class
{
    TParent ParentElement { get; }
}

public class ConfigurationElementCollection<TElement, TParent> : IConfigurationElementCollection<ParentedConfigElement<TParent>, TParent>
    where TElement : ParentedConfigElement<TParent>, new() 
    where TParent : class
{
    public TParent ParentElement { get; set; }

    protected ConfigurationElement CreateNewElement()
    {
        //**************************************************
        //COMPILER NO LONGER GIVES TYPE CONVERSION ERROR 
        //BECAUSE this IMPLEMENTS THE EXPECTED INTERFACE!
        //**************************************************
        return new TElement { ParentCollection = this };
    }
}

public class ParentedConfigElement<TParent> : ConfigurationElement where TParent : class
{
    internal IConfigurationElementCollection<ParentedConfigElement<TParent>, TParent> 
        ParentCollection { get; set; }

    protected TParent Parent
    {
        get
        {
            return ParentCollection != null ? ParentCollection.ParentElement : null;
        }
    }
}
公共接口IConfigurationElementCollection
其中TParentedConfig:ParentedConfigElement
地点:班级
{
t父元素{get;}
}
公共类ConfigurationElementCollection:IConfigurationElementCollection
其中TElement:ParentedConfigElement,new()
地点:班级
{
公共TParent ParentElement{get;set;}
受保护的ConfigurationElement CreateNewElement()
{
//**************************************************
//编译器不再给出类型转换错误
//因为这实现了预期的接口!
//**************************************************
返回新的远程通讯{ParentCollection=this};
}
}
公共类ParentedConfigElement:ConfigurationElement,其中TParent:class
{
内部IConfigurationElementCollection
父集合{get;set;}
受保护父母
{
得到
{
return ParentCollection!=null?ParentCollection.ParentElement:null;
}
}
}

由于
ParentedConfigElement
中的属性是内部的,因此如果您担心这类问题,也可以将接口设置为内部,以避免将此实现细节暴露给任何使用者

只有当目标对象上没有默认构造函数时,才需要Activator.CreateInstance。在我的示例中,我确实有一个默认构造函数。这只是我正在初始化的一个属性…不是构造函数参数。令人讨厌的是,CEC上的泛型约束与您提供的接口含义完全相同…但泛型约束感觉好像被忽略了。尽管如此,我还是很高兴你找到了一种不用
动态
类型就可以实现这一点的方法。使用组合是一种更好、正确的方法:)。删除我的答案。但是通用约束并没有说与接口完全相同的事情,它只是说第一个类型参数继承自
ParentedConfigElement
。这意味着
TElement
可分配给
ParentedConfigElement
,但该继承不扩展到泛型类型的类型参数。
protected ConfigurationElement CreateNewElement()
{
    return (TElement)Activator.CreateInstance(typeof(TElement), this);
}
public interface IConfigurationElementCollection<TParentedConfig, TParent> 
    where TParentedConfig : ParentedConfigElement<TParent>
    where TParent : class
{
    TParent ParentElement { get; }
}

public class ConfigurationElementCollection<TElement, TParent> : IConfigurationElementCollection<ParentedConfigElement<TParent>, TParent>
    where TElement : ParentedConfigElement<TParent>, new() 
    where TParent : class
{
    public TParent ParentElement { get; set; }

    protected ConfigurationElement CreateNewElement()
    {
        //**************************************************
        //COMPILER NO LONGER GIVES TYPE CONVERSION ERROR 
        //BECAUSE this IMPLEMENTS THE EXPECTED INTERFACE!
        //**************************************************
        return new TElement { ParentCollection = this };
    }
}

public class ParentedConfigElement<TParent> : ConfigurationElement where TParent : class
{
    internal IConfigurationElementCollection<ParentedConfigElement<TParent>, TParent> 
        ParentCollection { get; set; }

    protected TParent Parent
    {
        get
        {
            return ParentCollection != null ? ParentCollection.ParentElement : null;
        }
    }
}