2015-09-11 12 views
7

Targeting .net 4.0 Ich versuche, die folgenden Klassen zu bauen:Ungültige Cast von Typ Constrained C# Generisches

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

Da der Code Kommentar oben zeigt, gibt der Compiler einen Fehler:

Cannot implicitly convert type 'Shared.Configuration.ConfigurationElementCollection<TElement, TParent>' to 'Shared.Configuration.ConfigurationElementCollection<Shared.Configuration.ParentedConfigElement<TParent>,TParent>

Ich erwarte diesen Fehler nicht, weil der Compiler auch weiß, dass TElement ein Shared.Configuration.ParentedConfigElement<TParent> aufgrund der generischen Typ Einschränkung ich angegeben wurde.

Dennoch Ich denke, ich werde ausdrücklich die Art werfen Vergangenheit dieses Problem zu erhalten:

(ConfigurationElementCollection<ParentedConfigElement<TParent>,TParent>) this; 

Leider bekomme ich den gleichen Compiler-Fehler. Warum passiert dies? Was habe ich falsch gemacht? Und ohne auf dynamic Arten zurückgreifen, was kann ich tun, um das zu beheben?

Antwort

2

Ihr Problem ist, dass Sie eine Art haben CEC<A, B> und Sie versuchen, es auf eine Eigenschaft des Typs zuweisen CEC<C<A>,B>, die Sie nicht tun können, in der gleichen Weise wie List<string> nicht zugeordnet werden können, um Lagerung des Typs List<object>, obwohl string von object abgeleitet ist.

Eine saubere Lösung ohne implizite Operatoren oder dynamische Verwendung ist eine Schnittstelle zu verwenden:

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

Da die Eigenschaft in ParentedConfigElement intern ist, können Sie auch die Schnittstelle intern machen kann aussetzt diese Implementierung Detail auf alle Verbraucher zu vermeiden wenn dir das etwas angeht.

+0

Es ist anstößig, dass die generische Einschränkung für CEC genau dasselbe bedeutet wie die von Ihnen bereitgestellte Schnittstelle ... und doch fühlt sich die generische Einschränkung so an, als würde sie ignoriert. Trotzdem bin ich sehr froh, dass Sie einen Weg gefunden haben, dies ohne einen 'dynamischen' Typ zu machen. –

+0

Die Verwendung von Komposition ist viel besser und der richtige Weg, dies zu tun :). Entferne meine Antwort. –

+0

Aber die generische Constraint sagt nicht genau dasselbe wie die Schnittstelle, es sagt nur, dass der erste Typ Argument von ParenedConfigElement erbt. Dies bedeutet, dass "TElement" zu "ParentedConfigElement " zuweisbar ist, aber diese Vererbung erstreckt sich nicht auf die Typparameter generischer Typen. – Erik

4

auf https://stackoverflow.com/a/6529618/5071902

protected ConfigurationElement CreateNewElement() 
{ 
    return (TElement)Activator.CreateInstance(typeof(TElement), this); 
} 

You will need a constructor with this signature, setting the ParentCollection property.

Basierend Sie können versuchen, zu Reflexion verwenden. Werfen Sie einen Blick auf diese Antwort https://stackoverflow.com/a/6529622/5071902

+0

Die Activator.CreateInstance wird nur benötigt, wenn kein Standardkonstruktor für das Zielobjekt vorhanden ist. In meinem Beispiel habe ich tatsächlich einen Standardkonstruktor. Es ist nur eine Eigenschaft, die ich initialisiere ... kein Konstruktorparameter. –

Verwandte Themen