Reputation: 5220
I've been dealing a lot lately with abstract classes that use generics. This is all good and fine because I get a lot of utility out of these classes but now it's making for some rather ugly code down the line. For example:
abstract class ClassBase<T>
{
T Property { get; set; }
}
class MyClass : ClassBase<string>
{
OtherClass PropertyDetail { get; set; }
}
This implementation isn't all that crazy, except when I want to reference the abstract class from a helper class and then I have to make a list of generics just to make reference to the implemented class, like this below.
class Helper
{
void HelpMe<C, T>(object Value)
where C : ClassBase<T>, new()
{
DoWork();
}
}
This is just a tame example, because I have some method calls where the list of where clauses end up being 5 or 6 lines long to handle all of the generic data. What I'd really like to do is
class Helper
{
void HelpMe<C>(object Value)
where C : ClassBase, new()
{
DoWork();
}
}
but it obviously won't compile. I want to reference ClassBase without having to pass it a whole array of generic classes to get the function to work, but I don't want to reference the higher level classes because there are a dozen of those. Am I the victim of my own cleverness or is there an avenue that I haven't considered yet?
Upvotes: 0
Views: 162
Reputation: 110071
without having to pass it a whole array of generic classes to get the function to work
A small change might ease these calls. Move repeatedly specified types to the generic declaration of the class.
//before
Helper x = new Helper();
x.HelpMe<MyClass, string>(x);
x.HelpMe<MyClass, string>(y);
//after
Helper x = new Helper<string>();
x.HelpMe<MyClass>(x);
x.HelpMe<MyClass>(y);
//the change
class Helper<T>
{
void HelpMe<C>(object Value)
where C : ClassBase<T>, new()
{
DoWork();
}
}
Upvotes: 0
Reputation: 456417
Generics do have a tendency to propogate through the code, and they're seldomly used as "mixin" classes for that reason.
Thomas mentioned the one possibility of introducing an equivalent non-generic API. I would prefer revisiting the design and making these base classes non-generic if possible while maintaining full type safety. Whether or not this is actually possible is determined by your requirements.
There is one other possibility short of a re-design (or API duplication): dynamic. If you're willing to lose IntelliSense in your helper methods (and are willing to pay a very, very small runtime performance penalty), you can use dynamic in your helper method:
class Helper
{
void HelpMe<C>(object Value)
// where C : ClassBase<T>, new() // no longer needed, but should be documented
{
dynamic cObj = Activator.CreateInstance<C>(); // instead of "new C()"
cObj.PropertyDetail = ...;
cObj.Property = ...;
...
}
}
Upvotes: 0
Reputation: 243041
I suppose that your HelpMe
method would be used for initializing the concrete ClassBase<T>
type (a guess based on the constraints). To keep the code fully generic (if you need both T
and C
somewhere in the method), you probably need to keep both of the type parameters.
However, you could add a non-generic base class and then write something like this:
abstract class ClassBase {
object UntypedProperty { get; set; }
}
abstract class ClassBase<T> : ClassBase {
T Property { get; set; }
public override object UntypedProperty {
get { return Property; }
set { Property = (T)value; }
}
}
Then you could be to write the helper method like this:
void HelpMe<C>(object Value) where C : ClassBase, new() {
var n = new C();
c.UntypedProperty = Value;
}
Depending on your specific scenario, something along these lines might work and make the code a little bit simpler. However, you need to modify the base class to make this possible.
Upvotes: 2