How to use generic type constraint to enforce Add method existence

generic method with multiple type parameters c#
c# generic method where t multiple types
c# generic nullable constraint
c# new() constraint with parameters
c# generic method where t multiple classes
c# generic return type and parameter
c# where
c# generic class constructor

I have this code that uses the generic code:

    public static async Task<List<string>> GetDomainsAsync(IMemoryCache _cache)
    {
        return await ContextCache.CacheTryGetValueSet<List<String>>("SP_GET_DOMAINS", _cache);
    }

    public static async Task<Dictionary<String, String>> GetSettingsAsync(IMemoryCache _cache)
    {
        return await ContextCache.CacheTryGetValueSet<Dictionary<String, String>>("SP_GET_SETTINGS", _cache);
    }

And this is the generic method:

private static async Task<T> CacheTryGetValueSet<T>(String SPName, IMemoryCache _cache) where T : new()
{
          dynamic data = new T();


        ....

         while (reader.Read())
                                        {
                                            if (reader.FieldCount == 1)
                                            {
                                                data.Add(reader.GetString(0));
                                            }
                                            else if (reader.FieldCount == 2)
                                            {
                                                data.Add(reader.GetString(0), reader.GetString(1));
                                            }
                                        }
        ....

        return data;

        }

How can I make sure that T in-fact has an Add method? What generic type constraint can be added to make sure that only IEnumerable can be passed?


Whenever you have a generic method that contains if (typeof(T)), you are probably doing it wrong. The whole point of generics is that they operate exactly the same on a variety of types. If the types are too different (e.g. a dictionary that needs two fields versus a list that needs one) you end up writing non-generic code in a generic method, which just confuses things.

In this case you should probably have two methods, one that accepts one type parameter (which returns an IEnumerable<TItem>, which the caller can easily convert to a List or use in LINQ statements) and one that accepts two type parameters (which returns an IEnumerable<KeyValuePair<TKey,TValue>>, which the caller can convert to a dictionary or use in LINQ).

In addition, you probably should use yield return as rows become available so you don't have to read the entire rowset before the caller can start processing data. This will smooth out your performance, and neatly avoids the issue of figuring out whether there is an Add() method-- you don't need it.

Here is an example of a way you could rewrite your method to address these issues. It's a little tricky because you have to use a nested function to use yield return in an async Task:

public async Task<IEnumerable<TItem>> CacheTryGetValueSet<TItem>(string storedProcedureName, IMemoryCache cache)
{
    IEnumerable<TItem> Enumerate(SqlDataReader source)
    {
        while (source.Read())
        {
            yield return source.GetFieldValue<TItem>(0);
        }
    }

    var reader = await OpenReaderAsync(storedProcedureName);
    if (reader.FieldCount != 1) throw new ArgumentException("That type of cache doesn't return a single column.");
    return Enumerate(reader);
}

public async Task<IEnumerable<KeyValuePair<TKey,TValue>>> CacheTryGetValueSet<TKey,TValue>(string storedProcedureName, IMemoryCache cache)
{
    IEnumerable<KeyValuePair<TKey,TValue>> Enumerate(SqlDataReader source)
    {
        while (source.Read())
        {
            yield return new KeyValuePair<TKey, TValue>
            (
                source.GetFieldValue<TKey>(0),
                source.GetFieldValue<TValue>(1)
            );
        }
    }

    var reader = await OpenReaderAsync(storedProcedureName);
    if (reader.FieldCount != 2) throw new ArgumentException("That type of cache doesn't return two columns!");
    return Enumerate(reader);
}

Now the caller can call it this way:

public static async Task<List<string>> GetDomainsAsync(IMemoryCache _cache)
{
    return await ContextCache.CacheTryGetValueSet<string>("SP_GET_DOMAINS", _cache).ToList();
}

public static async Task<Dictionary<String, String>> GetSettingsAsync(IMemoryCache _cache)
{
    return await ContextCache.CacheTryGetValueSet<String, String>("SP_GET_SETTINGS", _cache).ToDictionary();
}

Constraints on type parameters, The constraint enables the generic class to use the Employee. T is a type constraint in the context of the Add method, and an unbounded type you want to enforce an inheritance relationship between two type parameters. When a generic type parameter is used as a constraint, it is called a naked type constraint. Naked type constraints are useful when a member function with its own type parameter needs to constrain that parameter to the type parameter of the containing type. In the following example, T is a naked type constraint in the context of the Add method.


There's no way to guarantee that there's a specific method on a generic at compile time, but you can enforce that the generic type implement an interface such as IList which has an Add method.

I.E.

public T Foo<T>() where T : IList, new()
{
  var list = new T();
  list.Add(...);
  return list;
}

where (generic type constraint), For example, you can declare a generic class, MyGenericClass in C# 8.0 and later, the nullability of the base class type is enforced. To allow nullable reference types, use the class? constraint, which can also attach constraints to type parameters of generic methods, Sign in to add your reaction. Generic Methods Constraints. Constraints are validations on type arguments which type of parameter we can pass in generic method. Generic class and Generic methods follow the same constraints. There are six types of constraints. where T : struct – Type argument must be a value type; where T : class – Type argument must be a reference type


You can try to use ICollection<string> interface be the generic type constraint where T : ICollection<string>, new().

Foundations of C++/CLI: The Visual C++ Language for .NET 3.5, function, 379–380 Collections namespace, 320 Collections::Generic namespace 35 /clr:oldSyntax, 36 /clr:pure, 34–35 /clr:safe, 34 /doc, 427 /FU (​Force Using), STL/CLR, 342 ConstrainedCopy method, Array class, 116 constraints adding to existence of operators, 331 constraints, generic types using, 312–320 class  Use type parameters as constraints on generic classes in scenarios in which you want to enforce an inheritance relationship between two type parameters. NotNull constraint. Beginning with C# 8.0 in a nullable context, you can use the notnull constraint to specify that the type argument must be a non-nullable value type or non-nullable reference type.


Database Systems For Advanced Applications '93, Methods No equivatent concept Composite object Entity types connected by ISPART-OF 4.1 Adding Methods to the OOER Model Methods are categorized into system For instance, the DBA can define methods to provide generic computations A simple trigger to enforce the referential constraint that exists between  Generic Constraints in C# In c#, generics are used to define a class or structure or methods with placeholders (type parameters) to indicate that they can use any of the types. Following is the example of defining a generic class with type parameter ( T ) as a placeholder with an angle ( <> ) brackets.


Generic Parameter Constraints for C#, It is also possible to create custom generic types and methods for In order to define a constraint, we need to use the where keyword, Enforcing constraints on specific type parameters allows you to For example, a simple application for a single purpose can exist without any generic implementation,  But, it's possible that the internals of the code use T (perhaps as an argument to a serializer?) and need to use specifically T and not the constraint class. You'll occasionally see that when the constraint is an interface paired with the new constraint and the guts of the method creates Ts for some reason.


Constraints | Using Generics in C# 2.0, These constraints enforce the types to conform to various rules. Take As a result, if the member does not exist in the type parameter, IComparable' in order to use it as parameter 'T' in the generic type or method 'BinaryTree<T>' To override this compiler restriction, you add the text new() after all other  The alternatives to a generic type are: A single type operating on the Object data type. A set of type-specific versions of the type, each version individually coded and operating on one specific data type such as String, Integer, or a user-defined type such as customer.