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

7

我正在针对.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;
        }
    }
}

正如上面的代码注释所指示的那样,编译器会给出一个错误信息:
“无法将类型'Shared.Configuration.ConfigurationElementCollection'隐式转换为类型'Shared.Configuration.ConfigurationElementCollection'”
我并不希望出现这个错误,因为编译器也知道由于我指定了泛型类型约束条件,TElement是一个 Shared.Configuration.ParentedConfigElement<TParent>
尽管如此,我认为我仍需要显式地进行类型转换,以克服这个问题。
(ConfigurationElementCollection<ParentedConfigElement<TParent>,TParent>) this;

不幸的是,我得到了相同的编译器错误。为什么会发生这种情况?我做错了什么?而且在不使用 dynamic 类型的情况下,我该怎么做才能解决这个问题?

2个回答

4

Activator.CreateInstance仅在目标对象上没有默认构造函数时才需要使用。在我的示例中,我确实有一个默认构造函数。我只是在初始化属性而不是构造函数参数。 - Brent Arias

2
您的问题在于您有一个类型为CEC<A, B>,并试图将其分配给类型为CEC<C<A>,B>的属性,这是不可能的,就像List<string>不能分配给类型为List<object>的存储一样,即使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;
        }
    }
}

由于ParentedConfigElement中的属性是内部的,如果您担心将此实现细节暴露给任何消费者,您也可以使接口内部化。


很讨厌的是,CEC<TElement, TParent> 上的通用约束符号恰好表示与您提供的接口完全相同的内容...但是通用约束符号似乎被忽略了。尽管如此,我非常高兴您找到了一种不使用“dynamic”类型就能使其工作的方法。 - Brent Arias
但是通用约束并不像接口那样明确地说明,它只是说明第一个类型参数继承自ParentedConfigElement<TParent>。这意味着TElement可以分配给ParentedConfigElement<TParent>,但继承并不扩展泛型类型的类型参数。 - Erik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接