Home Dashboard Directory Help
Search

Allow structural type constraints for generic type parameters by Rüdiger Klaehn


Status: 

Closed
 as Won't Fix Help for as Won't Fix


46
0
Sign in
to vote
Type: Suggestion
ID: 412848
Opened: 2/9/2009 3:15:57 AM
Access Restriction: Public
0
Workaround(s)
view

Description

Currently, there is a very large subset of C# language features that can not be used when using generics or interfaces:

-operators
-static properties
-constructors with arguments
-static methods
-implicit conversions

When not using generics or interfaces, C# is a very powerful and flexible language. But once you start using generics or interfaces it is very restricted. This might be OK if you use generics only for typed collections, but as soon as you use generics for something else this becomes very painful.

There are plenty of people that want to use C# generics for more than just typed collections. Just look at all the feedback items for "generic constructor", "generic static" or "generic operator". Unfortunately there is not a single common feedback item for this issue, so the importance of this feature is maybe overlooked.

For example with the current generics system it is impossible to perform arithmetic calculations on generic classes without sacrificing performance by using some kind of dynamic dispatch, or having to use very ugly hacks such as having a separate "calculator" type.


One solution would be to allow something like a structural type constraint similar to an interface constraint on type parameters. Here is a proposed syntax for specifying structural constraints. A new keyword "signature" is introduced. Inside a signature, everything is allowed that is allowed in a normal class/struct: static methods and fields, operators, type conversions, constructors with or without parameters etc. Of course you may not provide an implementation, since that is not part of the signature.

signature Numeric {
    public static Numeric operator +(Numeric a, Numeric b);
    public static Numeric operator *(Numeric a, Numeric b);
    public static Numeric Zero { get; }
    public static Numeric One { get; }
    ...
}

Now that a signature is defined, it is possible to use it as a constraint similar to an interface constraint:

public class Summer<T>
    where T:Numeric {
    public static T Sum(T[] items) {
        var result=T.Zero;
        foreach(var item in items)
            //this directly calls the + operator without any kind of dynamic dispatch
            //(interfaces or delegates),
            //so it is just as fast as using the concrete type directly, but more flexible
            result+=item;
        return result;
    }
}

public struct Complex<T>
    where T:Numeric
{
    readonly T re;
    readonly T im;
    ....
}

You get the idea.


The difference to an interface constraint is that the concrete type does not have to explicitly implement the signature. Instead it just has to conform to the signature by implementing the right methods/operators/constructors/properties with the right parameters and return values. So for example System.Double would conform to a signature requiring a + operator without having to implement it.


I realize that it is probably difficult to implement this feature since a new type would have to be generated for each T, so some changes to the CLR would be needed, just like the CLR generates a new type for each concrete struct T with an interface constraint.

And there might be some resistance from OO purists since two classes might have a method with the same name, the same parameters and return value and yet a different meaning. However C# has many features (for example extension methods, lambda expressions etc.) that are definitely not pure OO. Besides, C# is being sold as a multiparadigm language.


But something like this is definitely needed if C# wants to be a real alternative to C++ and not just a language for writing "business applications" and thin database frontends.

The proposed solution is somewhat similar to the C++ template approach, yet since it is explicit it allows much better IDE integration such as intellisense and refactoring as well as better error messages when a type does not conform to a signature.

If you can come up with something more clever than the solution I proposed, then by all means do it.

But something needs to be done about this issue. IMHO this is way more important than adding support for dynamic invocation to C# since it permits an entire class of problems to be solved in C# that can only be solved in C++ as of now. So please don't tell me that you don't have time for this.
Details
Sign in to post a comment.
Posted by lostmsu on 1/28/2011 at 2:06 AM
Take a look at my NTypeClasses project on CodePlex web site. http://ntypeclasses.codeplex.com/documentation
I have constraints defined in abstract classes and resolved during type loading. A separate checker can be run to verify all constraints can be resolved after compilation.

I'm going to provide something very similar to the thing you are requesting here.
Posted by Ibasa on 5/5/2009 at 7:43 AM
I don't belive any new keywords need to be added at all. Instead add the ability to add static methods/operators and consturctors to interfaces. Then whenever a generic method is constrained to that interface you can use those static methods and constructors in the method. I've used this example elsewhere but number class interfaces could be implement this way:

public interface INumeric
{
    static INumeric operator +(INumeric value);
    ...
    static INumeric operator %(INumeric left, INumeric right);
    
    static bool operator ==(INumeric left, INumeric right);
    ...
    static bool operator >=(INumeric left, INumeric right);
    
    static explicit operator INumeric(byte value);
    ...
    static explicit operator INumeric(decimal value);
    
    static explicit operator byte(INumeric value);
    ...
    static explicit operator decimal(INumeric value);
    
    static INumeric Parse(string s);
    ...
    static bool TryParse(string s, out INumeric result);
    ...
    
    static INumeric MaxValue { get; }
    static INumeric MinValue { get; }
}

public interface IReal : INumeric
{
    static IReal Nan { get; }
    static IReal PositiveInfinity { get; }    
    static IReal NegativeInfinity { get; }    
    static IReal Epsilon { get; }    
    
    static bool IsInfinity(IReal real);
    static bool IsNaN(IReal real);
    static bool IsNegativeInfinity(IReal real);
    static bool IsPositiveInfinity(IReal real);
}

public interface IIntegral : INumeric
{
    static IIntegral operator ~(IIntegral value);
    static IIntegral operator &(IIntegral left, IIntegral right);
    static IIntegral operator |(IIntegral left, IIntegral right);
    static IIntegral operator ^(IIntegral left, IIntegral right);
    static IIntegral operator >>(IIntegral left, int right);
    static IIntegral operator <<(IIntegral left, int right);
}

This would allow code such as:

public struct Vector4<T> where T : IReal
{
    public T X;
    public T Y;
    public T Z;
    public T W;
    
    public static UnitX { get {return new Vector4<T>((T)1, (T)0, (T)0, (T)0);}}
    
    public Vector4<T>(T x, T y, T z, T w)
    {
        X = x;
        Y = y;
        Z = z;
        W = w;
    }
    
    public static Vector4<T> operator +(Vector4<T> left, Vector4<T> right)
    {
        return new Vector4<T>(
            left.X + right.X,
            left.Y + right.Y,
            left.Z + right.Z,
            left.W + right.W);
    }
}

public void DoSomeVectorStuff()
{
    Vector4<float> fvector1 = new Vector4<float>(1.f, 3.f, 4.f, 5.f);
    Vector4<float> fvector2 = Vector4<float>.UnitX;
    Vector4<float> fvector = fvector1 + fvector2;
    Console.WriteLine("{0}, {1}, {2}, {3}", fvector.X, fvector.Y, fvector.Z, fvector.W);
}

Something like this however would not be allowed as it would require a virtual static lookup(?):

public INumeric DoSomeINumericStuff(INumeric a, INumeric b)
{
    return a + b;
}

What method should + call? What is the concrete return type, what if a is a float and b is byte? Due to these concerns I do not think this should be valid. It is easily converted to the following that should be valid tho:

public T DoSomeINumericStuff(T a, T b) where T : INumeric
{
    return a + b;
}

When the jitter reaches this method it will know what type T is, and it has a guarentee that it has a method op_addition(T left, T right) that returns T. It should be able to lookup the method generate the code for it.
Posted by Pavel Minaev [MSFT] on 5/1/2009 at 3:12 PM
Frankly, I would be content with INumeric<T> (or IArithmetic<T>, or whatever it ends up being called). In vast majority of cases I just want to use the operators. Static methods, not so much, and any use of static methods can be reasonably refactored to a non-static singleton class + interface, with the latter used for constraints. But the ability to constrain type parameters to common operators is an absolute must.

Also, please get rid of that silly prohibition on System.Enum and System.Delegate in type constraints. It serves absolutely no purpose, it is allowed by the CLR and C++/CLI, and it actually enables some common scenarios, some of which are indeed in BCL itself (Expression<T> comes to mind, and it would also allow to safely generify methods of Enum itself).
Posted by Pavel Minaev [MSFT] on 5/1/2009 at 3:09 PM
> In the System.Reflection.Emit library, there are many common methods and properties between the MethodInfo and ConstructorInfo classes, as there should be because a constructor is a special type of of method. And I agree, those common methods and properties should be defined on an interface, which both classes should implement.

MethodBase, from which both MethodInfo and ConstructorInfo inherit precisely for that reason, is no interface, but you can still use it in generic type constraints (and elsewhere)...
Posted by JGWeissman on 4/3/2009 at 12:27 PM
"So if you want to write things to match a structural constraint, you have to know about the structural constraint. But then it might as well be an interface, and you might as well implement it."

In the System.Reflection.Emit library, there are many common methods and properties between the MethodInfo and ConstructorInfo classes, as there should be because a constructor is a special type of of method. And I agree, those common methods and properties should be defined on an interface, which both classes should implement. But I don't have the opportunity to actually do that, those classes are defined by Microsoft. These structural constraints, or any sort of implicit interface implementation, would enable me to more easily work around Microsoft's poor library design. You can argue that this benefit does not justify the dealing with the implementation issues of virtual dispatch on methods that aren't known at compile time to implement the same virtual method, but don't pretend the benefit doesn't exist.
Posted by Rüdiger Klaehn on 4/1/2009 at 3:28 PM
Another thing: if structural type constraints are so useless, then why are they being used by C# itself all the time?

In the following example, Enumerable can be enumerated even though it does not implement enumerable, as long as it has a method of the correct signature called GetEnumerator(). A structural type constraint.

And Collection can be used with a collection initializer even though it does not implement ICollection. Again, the thing the compiler looks for is an Add method with the right signature. Another structural type constraint.

    class Enumerable
    {
        public IEnumerator<int> GetEnumerator()
        {
            for (int i = 0; i < 10; i++)
                yield return i;
        }
    }
    class Collection : IEnumerable<int>
    {
        public void Add(int x) { Console.WriteLine(x); }
        IEnumerator<int> IEnumerable<int>.GetEnumerator() { throw new NotImplementedException(); }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var t = new Collection() { 1, 2, 3 };
            foreach (var i in new Enumerable())
                Console.WriteLine(i);
            Console.ReadLine();
        }
    }
Posted by Rüdiger Klaehn on 4/1/2009 at 1:47 PM
I disagree that interfaces are better in all cases than structural constraints. In _my_ practice structural constraints would be a huge benefit.

By the way: what is the point of having a feedback system if you don't listen to feedback. There are several very highly rated feedback items about the whole generics and operators mess and about the lack of static method and parameterized constructor constraints in generics. And there is not a _single_ highly rated feedback item about introducing dynamic dispatch to C#.

In all my years of C# programming I never had any need for a dynamic keyword, while the lack of operators with generics causes me huge problems almost every day. But I guess what I am doing with C# (high performance mission critical code for space operations) is not what you intended the language to be used for. You seem to be thinking more about office interop.

By the way: it really makes no sense to be against structural type constraints because they are not precise enough, and then add a feature like dynamic to the language.

And if you really think that interfaces are the best way to describe similarities, then why are there so little interfaces in mscorlib? A readonly list interface would be very useful. Or a set interface. Or the arithmetic interfaces I proposed in another ignored feedback item: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94264 .

Of course I can define my own, but then I have the situation where I use three different libraries that each provide their own interface.
Posted by Microsoft on 3/31/2009 at 3:29 PM
Thank you for your proposal.

This is a proposal that we have seen in various shapes a number of times. You mention a number of different things that are hard with the current generic constraints - some of those I agree are limiting, others less so.

We are looking into possible ways of addressing a number of these situations using interfaces - allowing interfaces to express contracts on static members as well as instance members is the general idea.

Interfaces have to still be explicitly implemented though. I believe that is mostly a good thing and find it less likely that we will pursue approaches that describe the type structurally.

In most situations structural constraints tend to provide little or no benefit in practice. If you constrain the type parameter to have a method with a certain name and shape, you can be certain that whoever wants to supply a type argument for your generic thing wrote his type with slightly different capitalization in the name, different ordering of parameters, using a base type instead of a derived type etc etc. In practice, members almost never match by accident.

So if you want to write things to match a structural constraint, you have to know about the structural constraint. But then it might as well be an interface, and you might as well implement it.

Overloadable operators may be an exception - the list of these is so short and the patterns in their signature so common that it may make sense to describe these structurally. Even there though I think that an interfaced based approach would probably be quite sufficient in practice.

Whatever happens here, it won't be for the upcoming release, so I'll resolve this suggestion as "Won't Fix". However, this is one that will get more airtime in our design effort for the following release. Whether we actually solve it or not I cannot promise, but we will try.

Thanks again,

Mads Torgersen, C# Language PM
Posted by Jon Skeet on 3/19/2009 at 12:33 PM
Rudiger's proposal is very similar to the one I make in this blog post: http://msmvps.com/blogs/jon_skeet/archive/2009/03/19/1646206.aspx

I call the concept "static interfaces" seeing as it describes which members are exposed statically. That has the benefit of not needing a new keyword :)

But yes, I'd like to see this kind of thing in *some* shape or form.
Posted by alanfo on 2/27/2009 at 11:14 AM
After thinking long and hard about this, I'd come up with virtually the same solution as Rudiger so he's saved me the trouble of suggesting it :)

It seems to me that the only drawback is that the CLR would need to be changed.

Some further thoughts:

1. As the C# team have always been reluctant to introduce any new keywords for fear of breaking existing code, you might need to call the new structure a 'signature interface' or something like that where 'signature' is a contextual keyword.

2. Signature interfaces could support multiple inheritance to enable you to combine previously defined signatures (or even normal interfaces) into a single signature. If there were any duplicated members, a compile time error would occur.

3. Some basic signatures (for example for arithmetic types) could be included in the .NET Framework.

4. There could be a best practice whereby signatures began with 'S' analogous to interfaces beginning with 'I'.
Sign in to post a workaround.