Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic type constraint to prohibit certain types?

Tags:

c#

generics

I'd like to create a generic method:

MvcHtmlString MyMethod<T>(this HtmlHelper html, string title, IEnumerable<T> value)

I have many variables in my code which are List<T>, IEnumerable<T>, xxxCollection, T[], etc... This method also has an overload which will take non enumerable values. Is it possible to prohibit specific classes (such as string) in the type parameter constraints?

I've already created one overload like this:

MvcHtmlString MyMethod<T>(this HtmlHelper html, string title, object value)

This overload is good for dealing with individual values, but dealing with collections of values requires a slightly different implementation. However, string implements IEnumerable so all my string variables will be sent to the wrong overload unless I can tell the compiler that they should be excluded.

like image 902
Zarepheth Avatar asked Sep 15 '25 07:09

Zarepheth


2 Answers

No. Generic type parameters constraints can only enforce positive constraints not negative constraints.

Run-time type checking is probably the way to go:

MvcHtmlString MyMethod<T>(this HtmlHelper html, string title, IEnumerable<T> value)
{
    if (typeof(T) == typeof(string))
    {
        throw new IllegalOperationException(...);
    }
}

If all the types you want to pass to your method inherit from the same base class, or implement the same interface, you can use jrummell's suggestion of a single constraint to enforce inheritance.

Although it's much more inelegant, another way of doing this if you want to support heterogeneous types would be providing enough overloads to handle your particular use cases:

// supports int, float, DateTime, etc.
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<int> value)
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<float> value)
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<DateTime> value)

// supports implementations of MyInterface
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<IMyInterface> value)

In the above example you would not be able to call MyMethod<string> since it doesn't satisfy any of the provided type constraints. Note that you can't use type constraints for this though as they are not part of the method signature.

like image 142
p.s.w.g Avatar answered Sep 17 '25 05:09

p.s.w.g


Unfortunately, no.

You can only specify the following types of constraints:

where T : struct               // T must be a value type
where T : class                // T must be a reference type
where T : new()                // T must have a parameterless constructor
where T : <base class name>    // T must inherit from <base class>
where T : <interface name>     // T must implement <interface>
where T : U                    // T must inherit from U, where U is another
                               // generic parameter

Reference: Constraints on Type Parameters.

Now, you can use some of these to restrict, but not to lock types out, only to specify the types you allow, such as the interface implementation constraint.

You can use the interface or base class constraint to specifically say "the types I allow must all have the following characteristic". I would say this would be your best option.

Are you sure generics is the right tool here?

like image 35
Lasse V. Karlsen Avatar answered Sep 17 '25 03:09

Lasse V. Karlsen