2

I have a couple of classes with the following pattern that I am uncomfortable with. Is this the only way to do this, or are their other design patterns that would be more reliable.

public class MyClass1<T,K> where T: MyClass2<K>
{
...
public MyClass1(K param1) {...} // example of constructor, other constructors do not use K.
...
}

I feel that it should be possible for MyClass1 to work out K from T, so that it does not have to be passed in from outside. However I've revisited this class many times and while it works and I can't spot another way to do it, I'm not comfortable with it. It seems open to programmers consuming this class being unsure what the second type parameter should be.

T itself does not need to be generic, just inherited from MyClass2. In fact in my example of this MyClass2 is abstract and all the classes inherited from it are not generic. I don't like that the programmer consuming MyClass1 needs to know the type parameter of the generic base class underlying whatever they are using for T.

The good thing is that the consuming code won't compile if K is not the right type to go with T. But it still seems open to confusion when trying to consume this class.

So is there a way this can be done where there is only one type parameter and the other is worked out from it?


Here is the concrete example. If the answer is that this is overall not the right design then I'm open to comments on those lines.

I started with the Enumeration class from LosTechies here, but I wanted the value to be something other than an int, and in fact had a need for the Enumeration class to be generic so the value type could be specified differently at different times. (Note this is C#6.0 syntax).

public abstract class Enumeration<K> : IComparable where K : IComparable, IEquatable<K>
{
protected K Value { get; }
public string DisplayName { get; }

... // other methods from the LosTechies enumerator class.

    private static T parse<T, V>(V value, string description, Func<T, bool> predicate) where T : Enumeration<K>, new()
{
    var matchingItem = GetAll<T>().FirstOrDefault(predicate);

    if (matchingItem == null)
    {
        //var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
        //throw new ApplicationException(message);

        throw new InvalidEnumerationException<T,K>(value.ToString(),description);
    }

    return matchingItem;
}

I didn't like the use of ApplicationException in the LosTechies code, as I understand Microsoft no longer advise the use of that. So I want to create my own more specific exception that can generate the exception message given the invalid value.

public class InvalidEnumerationException<T,K> : Exception, ISerializable
    where T : Enumeration<K>
    where K: IComparable, IEquatable<K>
{
public InvalidEnumerationException(K value, string description)
    :base(@"{value.ToString()}' is not a valid {description} in {typeof(t)}")
{}
... // other standard exception constructors
}

I've simplified this somewhat as I've made some other changes from the original enumeration class which I don't think are relevant here. Hopefully this captures the relevant parts for what I am doing.

What I don't like about it is that consuming InvalidEnumerationException requires you to pass a type parameter of Enumeration AND K separately. Classes that inherit from Enumeration generally AREN'T generic themselves, but based on a fixed signature of Enumeration.

I realised while preparing this concrete example that InvalidEnumerationException is raised from within Enumeration therefore that already knows K. The problem only comes when InvalidEnumerationException is raised outside Enumeration. Is that likely... well so far only in my Unit Tests of InvalidEnumerationException. However I can forsee some classes derived from Enumeration, or one derived from a class derived from Enumeration may want to have their own implementation of the FromValue/FromDescription methods of the enumeration which throw this exception themselves.

It really seems like something is wrong with the design when there are two type parameters and one of them must match the type parameter of the other. It feels like I should be able to just pass T, in my example just pass the enumeration.

  • 4
    This seems clear enough to me. What, then, is the purpose of K? Can you provide a more specific and concrete example, not simply MyClass, that illustrates the purpose of this technique? – Robert Harvey Dec 13 '15 at 0:58
  • When you say "it should be possible for MyClass1 to work out K from T", that implies there's a specific relationship between K and T it could be using, in addition to the inheritance relationship that already is being used. What extra relationship did you have in mind? And as Robert said, a concrete example would help. – Ixrec Dec 13 '15 at 13:36
  • K is a simple type, an int, int64, string which is used to define a member in MyClass2, but a constructor of MyClass1 also needs to have a parameter of that type. It feels wrong that when constructing MyClass1 you have to pass it as a type parameter when it's already defined in the concrete signature of T/MyClass2 which is also passed as a type parameter. I've added a more detailed example. Maybe I need to redesign overall... not sure. – RosieC Dec 14 '15 at 19:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.