Targeting .net 4.0, I'm trying to build the following classes:
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;
}
}
}
As the code comment above indicates, the compiler gives an error:
Cannot implicitly convert type 'Shared.Configuration.ConfigurationElementCollection<TElement, TParent>' to 'Shared.Configuration.ConfigurationElementCollection<Shared.Configuration.ParentedConfigElement<TParent>,TParent>
I don't expect this error, because the compiler also knows that TElement
is a Shared.Configuration.ParentedConfigElement<TParent>
due to the generic type constraint I specified.
Still, I figure I will expressly cast the type to get past this issue:
(ConfigurationElementCollection<ParentedConfigElement<TParent>,TParent>) this;
Unfortunately, I get the same compiler error. Why is this happening? What did I do wrong? And without resorting to dynamic
types, what can I do to fix this?
Constraints inform the compiler about the capabilities a type argument must have. Without any constraints, the type argument could be any type. The compiler can only assume the members of System.Object, which is the ultimate base class for any .NET type. For more information, see Why use constraints.
C# provides a standard set of built-in types. These represent integers, floating point values, Boolean expressions, text characters, decimal values, and other types of data. There are also built-in string and object types. These types are available for you to use in any C# program.
Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type. They declare capabilities that the type argument must have, and must be placed after any declared base class or implemented interfaces.
Based on 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.
You can try using reflection too. Take a look at this answer https://stackoverflow.com/a/6529622/5071902
Your problem is that you have a type CEC<A, B>
and you trying to assign it to a property of type CEC<C<A>,B>
which you can't do, in much the same way as List<string>
cannot be assigned to storage of type List<object>
even though string
derives from object
.
A clean solution without using implicit operators or dynamic is to use an interface:
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;
}
}
}
Since the property in ParentedConfigElement
is internal, you can also make the interface internal to avoid exposing this implementation detail to any consumers, if that sort of thing is a concern for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With