Is There a Generic Constraint I Could Use for the + Operator

Is there a generic constraint I could use for the + operator?

There are no such devices in C#. A few options are available, though:

  • in C# 4.0 and .NET 4.0 (or above), use dynamic, which supports + but offers no compile time checking
  • in .NET 3.5 (or above), MiscUtil offers an Operator class which makes operators available as methods - again, without any compile-time checking

So either:

return (dynamic)left.Evaluate(context) + (dynamic)right.Evaluate(context);

or

return Operator.Add(left.Evaluate(context), right.Evaluate(context));

Define a generic that implements the + operator

This is a pretty commonly requested new feature for C#: the ability to specify more generic parameter constraints than the ones we already have. Operators are among the most frequently asked. However, C# does not currently support this.

Possible workarounds:

  • Pass a delegate to any method that needs to do addition. This is the most type-safe option, but of course it’s annoying if you need to call such a method often. For example:

    public class Generic<T> {
    public void DoSomething(T anItem, T anotherItem, Func<T, T, T> add) {
    // instead of
    Blah(anItem + anotherItem);
    // have to write:
    Blah(add(anItem, anotherItem));
    }
    }

    Generic<int> genInt = ...;
    // and then instead of ...
    genInt.DoSomething(1, 2);
    // have to write:
    genInt.DoSomething(1, 2, (a, b) => a + b);
  • Declare your own interface IAddable. Then you can use it as a generic type parameter constraint, but obviously you can’t use int as the parameter then. You would have to use a struct of your own that contains just an int and which implements IAddable:

    public interface IAddable<T> {
    T Add(T other);
    }
     
    public struct Integer : IAddable<Integer> {
    public int Value;
    public Integer(int value) { Value = value; }
    public Integer Add(Integer other) { return new Integer(Value + other.Value); }
    }

    // then instead of
    Generic<int> blah = ...;
    // have to write:
    Generic<Integer> blah = ...;
  • dynamic. Another possible workaround is to use dynamic, but this is rather hacky and completely unsafe: it will let you pass in any type and call any method or operator, and only crash at runtime, not at compile-time.

How do I define a generic constraint so that I can use the ?? coalesing operator

You have to indicate that TDatabase is a reference type

where TDatabase : class, IDatabase

MSDN, Constraints on Type Parameters (C# Programming Guide)

where T : class The type argument must be a reference type; this applies also to any class, interface, delegate, or array type.

MSDN, ?? Operator (C# Reference):

The ?? operator is called the null-coalescing operator and is used to
define a default value for nullable value types or reference types. It
returns the left-hand operand if the operand is not null; otherwise it
returns the right operand.

Can't operator == be applied to generic types in C#?

"...by default == behaves as described above for both predefined and user-defined reference types."

Type T is not necessarily a reference type, so the compiler can't make that assumption.

However, this will compile because it is more explicit:

    bool Compare<T>(T x, T y) where T : class
{
return x == y;
}

Follow up to additional question, "But, in case I'm using a reference type, would the the == operator use the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?"

I would have thought that == on the Generics would use the overloaded version, but the following test demonstrates otherwise. Interesting... I'd love to know why! If someone knows please share.

namespace TestProject
{
class Program
{
static void Main(string[] args)
{
Test a = new Test();
Test b = new Test();

Console.WriteLine("Inline:");
bool x = a == b;
Console.WriteLine("Generic:");
Compare<Test>(a, b);

}

static bool Compare<T>(T x, T y) where T : class
{
return x == y;
}
}

class Test
{
public static bool operator ==(Test a, Test b)
{
Console.WriteLine("Overloaded == called");
return a.Equals(b);
}

public static bool operator !=(Test a, Test b)
{
Console.WriteLine("Overloaded != called");
return a.Equals(b);
}
}
}

Output

Inline:
Overloaded == called

Generic:

Press any key to continue . . .

Follow Up 2

I do want to point out that changing my compare method to

    static bool Compare<T>(T x, T y) where T : Test
{
return x == y;
}

causes the overloaded == operator to be called. I guess without specifying the type (as a where), the compiler can't infer that it should use the overloaded operator... though I'd think that it would have enough information to make that decision even without specifying the type.

Is possible to combine Generic Constraint with operator overloading?

At this point of time (May 2018), the only thing you can do is add a constraint to IComparable<T> to the class definition and use the CompareTo method instead of the <.

public class MyClass<T> where T : IComparable<T>
{
public void MyMethod(T a, T b)
{
//some code
var result = a.CompareTo(b) < 0; // a < b
//some code
}
}

This will cover <, <=, >, >= (and technically even the ==, but it is better to use IEquatable<T>), for the == you can add a constraint to IEquatable<T> to the class definition and use the Equals method instead of the == (and the !Equals instead of the !=).

For math/bitwise operators at this point there is no hope. There are always requests for this feature in the C# wishlist, but they always get postponed. In the C# 8.0 this feature won't be present (probably). See the official list of candidates. See Generic C# Code and the Plus Operator for an older question about this. Note that the solutions given won't give errors at compile time if you try to use a non-existing operator, but will give those errors at runtime.

Is there a Go generic type constraint that captures the ability to use a type as a key in a map?

The predeclared comparable constraint is the correct catch-all constraint for map keys, as it is implemented by types that support == and != (condition for being used as map keys), but not interfaces 1.

This is mentioned here: https://go.dev/ref/spec#Type_constraints

The predeclared interface type comparable denotes the set of all
non-interface types that are comparable. Specifically, a type T
implements comparable if:

  • T is not an interface type and T supports the operations == and != 2
  • T is an interface type and each type in T's type set implements comparable

Even though interfaces that are not type parameters can be compared (possibly causing a run-time panic) they do not implement comparable.

This is an important gotcha, because basic interface types normally do support the equality operators — what is compared is their dynamic types/values.

Therefore, your interface List[X] can be used as a map key directly, as in map[List[int]]string{}, but it does not implement comparable because it has an infinite type set (it has no terms, so any type implements it). And Cons doesn’t implement it either because it has a field of type List[X]. There is no "weaker" constraint for this.

Consider that constraints that embed comparable are also valid for map keys, so if you really need the method isList() in the function body, you can define a constraint like this, and have your lists-that-are-map-key structs implement that, instead of declaring an interface field:

// may use this as a constraint
type List interface {
comparable
isList() bool
}

1: the quote from the specs hints there are interface types that implement comparable, but it's effectively not possible to instantiate comparable with any interface at all: interfaces with only methods have an infinite type set, and interfaces with type terms can't be used anywhere except as constraints.

2: this rule actually doesn't cover non-interface types that support ==, like type S struct { data any }, but these types still can't instantiate comparable https://go.dev/play/p/N-pmE0XC-hB. This is a bug in the spec.

In Go generics, why can't I use comparable constraint with order operators?

comparable is the constraint for types that support equality operators == and !=. The language spec defines this in Type constraints.

Notably, this includes anything that can be used as a map key, including arrays, structs with comparable fields but not interfaces.

It is true that in the Go language specifications, the comparison operators include order operators as (<, >, <=, >=). This choice of terminology probably is what confuses you. However the specs also disambiguate:

The equality operators == and != apply to operands that are comparable. The ordering operators <, <=, >, and >= apply to operands that are ordered.

In Go 1.18 the available constraint that supports the order operators such as > and < is constraints.Ordered1:

type Ordered interface {
Integer | Float | ~string
}

1: note that the package golang.org/x/exp is experimental. Its contents aren't guaranteed to be backwards compatible with new Go releases, but you can always copy-paste the definitions you need into your own code

Generic Constraint for allowing bitwise operations?

There is no appropriate generic type constraint for this situation. You can apply bitwise operations to ints, so convert the enum value to int. T cannot be cast to int directly; however, you can make the detour over object.

public static string ToFlags<T>(this T val)
where T : struct
{
return string.Join(",", Enum.GetValues(typeof(T))
.Cast<T>()
.Select(enumEntry => ((int)(object)enumEntry & (int)(object)val) > 0
? enumEntry.ToString() : "")
.Where(f => !string.IsNullOrWhiteSpace(f)));
}

Note: There is a similar situation with numbers. You can't say where T : number and apply numeric operators on values of type T.

Also you can make val a T and you don't have to pass the type of T as method argument. You are passing it as generic type argument already. This is sufficient. The compiler is also smart enough to infer T, so you don't have to specify it when calling the method.

// Test
Flags item = Flags.COMMAND_ACTION | Flags.COMMAND_MSG;
Console.WriteLine(item.ToFlags());

You can specify struct as generic type constraint, as struct stands for value types. It is not perfect, but better than no constraint at all.



Related Topics



Leave a reply



Submit