C# Generic "Where Constraint" with "Any Generic Type" Definition

C# generic where constraint with any generic type definition?

There are typically 2 ways to achieve this.

Option1: Add another parameter to IGarrage representing the T which should be passed into the IGenericCar<T> constraint:

interface IGarrage<TCar,TOther> where TCar : IGenericCar<TOther> { ... }

Option2: Define a base interface for IGenericCar<T> which is not generic and constrain against that interface

interface IGenericCar { ... }
interface IGenericCar<T> : IGenericCar { ... }
interface IGarrage<TCar> where TCar : IGenericCar { ... }

c# where for generic type constraint class may NOT be

Given the explanation in the comment, why you think you need this: No,
you don't need to exclude int from the generic type.

When overloaded methods in a class (methods that differ by type of their parameters only) are used in a generic class, then the decision which of the methods is called is already made while the Generic class is compiled independently of the concrete type then used later on.

Example:

class Test<T>
{
public void Trigger(T test)
{
// Will always call Internal(object) and never call Internal(int) even when T is int.
Internal(test);
}

private void Internal(int test)
{
MessageBox.Show("Triggered int");
}

private void Internal(object test)
{
MessageBox.Show("Triggered object");
}
}

private void buttonTest_Click(object sender, EventArgs e)
{
Test<int> test = new Test<int>();
test.Trigger(42);
}

The output is

"Triggered object"

Even when T is int, the overloaded Internal method that takes an int is never called because the decision that Trigger calls the Internal method that expects an object is already made for the whole generic class independently of the concrete type used.


The same is true when you use an OrderedDictionary internally. myOrderedDictionary[x] where x is a generic type will always use the index property that accesses entries by key and not the one that accesses them by order because this decision is made based on the known constraints of the generic type independently of the concrete type used later on.

class TestDictionary<TKey, TValue> 
{
OrderedDictionary orderedDictionary = new OrderedDictionary();

public void Add(TKey key, TValue value)
{
orderedDictionary.Add(key, value);
}

public TValue GetByIndex(int index)
{
return (TValue)orderedDictionary[index];
}

public TValue GetByKey(TKey key)
{
return (TValue)orderedDictionary[key];
}
}

private void buttonTest_Click(object sender, EventArgs e)
{
TestDictionary<int, string> test = new TestDictionary<int, string>();

test.Add(42, "Test");

MessageBox.Show(test.GetByIndex(0)); // Correct output "Test"
MessageBox.Show(test.GetByKey(42)); // Correct output "Test"
}

How this where generic type constraint work?

Note how your ITest interface and B class don't have an explicit access modifier; when one is not provided the default is internal (for Top-level types), and you are implementing them in your MyClass class which is public (which is more accessible than internal, therefore, the compiler error). The solution is to change the access modifier of your types to public in this case, or alternatively, make your MyClass class internal instead of public.

 //my own
public interface ITest{}

public class B : ITest
{
public B()
{

}
}

public class MyClass<T> : B where T : ITest, new()
{

}

More info about access modifiers can be found here:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/accessibility-levels

How to add a C# generic type constraint on another generic base class?

After checking that it compiles, I will upgrade it to an answer.

From your question and comments, you want the parameter to be Entity<Something>. You do not need to use the parametrized types directly as a type, it can be use to parametrize a parameter.

So just do

 public void Update(Entity<T1> entity) where ....

How to constrain nested generic types of a generic method

Why does this compile for the interface, but not for the method?

Well, you are declaring TOrder and TArticle in IOrderPosition interface but not in GetOrderPositionOfOrder method.

You need to declare these generic parameters in method declaration:

public List<TOrderPosition> GetOrderPositionOfOrder<TOrder, TArticle, TOrderPosition>(long? id)
where TOrder : IOrder
where TArticle : IArticle
where TOrderPosition : IOrderPosition<TOrder, TArticle, TOrderPosition>
{
...
}

And call it like this:

var list = GetOrderPositionOfOrder<Order, Article, OrderPosition>(5);

But if you want to call GetOrderPositionOfOrder like:

var list = GetOrderPositionOfOrder<OrderPosition>(5);

You can make IOrderPosition covariant in TOrder and TArticle:

interface IOrderPosition<out TOrder, out TArticle, TOrderPosition>
where TOrder : IOrder
where TArticle : IArticle
where TOrderPosition : IOrderPosition<TOrder, TArticle, TOrderPosition>
{
long? id { get; set; }
TOrder order { get; }
TArticle Article { get; }
List<TOrderPosition> subPositions { get; set; }
}

Note that Order and Article must be getter-only properties (but these properties in OrderPosition can have set accessor).

And the method:

public List<TOrderPosition> GetOrderPositionOfOrder<TOrderPosition>(long? id)
where TOrderPosition : IOrderPosition<IOrder, IArticle, TOrderPosition>
{
...
}

Doing this you can make the calls like GetOrderPositionOfOrder<OrderPosition>(5).

F# Generic constraint to have one generic type inherit from another

As from the F# spec:

New constraints of the form type :> 'b are solved again as type = 'b.

There are some popular F# language suggestions that aim to solve this, see:

https://github.com/fsharp/fslang-suggestions/issues/255

https://github.com/fsharp/fslang-suggestions/issues/162

Is there a constraint that restricts my generic method to numeric types?

This constraint exists in .Net 7.

Check out this .NET Blog post and the actual documentation.

Starting in .NET 7, you can make use of interfaces such as INumber and IFloatingPoint to create programs such as:

using System.Numerics;

Console.WriteLine(Sum(1, 2, 3, 4, 5));
Console.WriteLine(Sum(10.541, 2.645));
Console.WriteLine(Sum(1.55f, 5, 9.41f, 7));

static T Sum<T>(params T[] numbers) where T : INumber<T>
{
T result = T.Zero;

foreach (T item in numbers)
{
result += item;
}

return result;
}

INumber is in the System.Numerics namespace.

There are also interfaces such as IAdditionOperators and IComparisonOperators so you can make use of specific operators generically.

C# Generics: Constraining T where T : Object doesn't compile; Error: Constraint cannot be special class 'object'

There is no difference between the two constraints, except for that one is disallowed for being useless to explicitly state.

The C# 4.0 language specification (10.1.5 Type parameter constraints) says two things about this:

The type must not be object. Because all types derive from object,
such a constraint would have no effect if it were permitted.

...

If T has no primary constraints or type parameter constraints, its
effective base class is object.

In your comment, you said that you were trying to make T be of type Void. Void is a special type that indicates that there is no return type and cannot be used in place of T, which requires an appropriate concrete type. You will have to create a void version of your method and a T version if you want both.

Go generics: type constraint for map keys?

Go 1.20 (February 2023)

comparable is the correct catch-all constraint for map keys.

All types that are comparable as per the Go spec, even if the comparison may panic at run time, can satisfy the comparable constraint. Your code will compile as expected in 1.20.

This finally fixes the inconsistency in previous Go version about spec-comparable types vs comparable types. See below for details.

Go 1.18 and 1.19

The predeclared comparable constraint is the correct constraint for map keys, however it can be instantiated only by strictly comparable types, i.e. types that support == and != (condition for being used as map keys) but won't panic at run time. This excludes interfaces1.

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.



Related Topics



Leave a reply



Submit