Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wildcard equivalent in C# generics

Let's say I have a generic class as follows:

public class GeneralPropertyMap<T> { } 

In some other class I have a method that takes in an array of GeneralPropertyMap<T>. In Java, in order to take in an array that contains any type of GeneralPropertyMap the method would look like this:

private void TakeGeneralPropertyMap(GeneralPropertyMap<?>[] maps) { } 

We use the wildcard so that later we can call TakeGeneralPropertyMap passing a bunch of GeneralPropertyMap with any type for T each, like this:

GeneralPropertyMap<?>[] maps = new GeneralPropertyMap<?>[3]; maps[0] = new GeneralPropertyMap<String>(); maps[1] = new GeneralPropertyMap<Integer>(); maps[2] = new GeneralPropertyMap<Double>(); //And finally pass the array in. TakeGeneralPropertyMap(maps); 

I'm trying to figure out an equivalent in C# with no success. Any ideas?

like image 883
AxiomaticNexus Avatar asked Mar 22 '13 16:03

AxiomaticNexus


People also ask

What is a wildcard in C?

Alternatively referred to as a wild character or wildcard character, a wildcard is a symbol used to replace or represent one or more characters. The most common wildcards are the asterisk (*), which represents one or more characters and question mark (?) that represents a single character.

Which wildcard character can be used to replace?

A wildcard can replace one or more characters in a string of text or numbers. The most common wildcard is the asterisk (*). It's important to note that wildcard searches are case sensitive.

What does the wildcard operator * do?

An asterisk (*) may be used to specify any number of characters. It is typically used at the end of a root word, when it is referred to as "truncation." This is great when you want to search for variable endings of a root word.


2 Answers

Generics in C# make stronger guarantees than generics in Java. Therefore, to do what you want in C#, you have to let the GeneralPropertyMap<T> class inherit from a non-generic version of that class (or interface).

public class GeneralPropertyMap<T> : GeneralPropertyMap { }  public class GeneralPropertyMap {     // Only you can implement it:     internal GeneralPropertyMap() { } } 

Now you can do:

private void TakeGeneralPropertyMap(GeneralPropertyMap[] maps) { } 

And:

GeneralPropertyMap[] maps = new GeneralPropertyMap[3]; maps[0] = new GeneralPropertyMap<String>(); maps[1] = new GeneralPropertyMap<Integer>(); maps[2] = new GeneralPropertyMap<Double>(); TakeGeneralPropertyMap(maps); 
like image 151
Daniel A.A. Pelsmaeker Avatar answered Sep 20 '22 13:09

Daniel A.A. Pelsmaeker


While, as others have noted, there's no exact correspondence to wildcards in c#, some of their use cases can be covered with covariance/contravariance.

public interface IGeneralPropertyMap<out T> {} // a class can't be covariant, so                                          // we need to introduce an interface...  public class GeneralPropertyMap<T> : IGeneralPropertyMap<T> {} // .. and have our class                                                             // inherit from it  //now our method becomes something like private void TakeGeneralPropertyMap<T>(IList<IGeneralPropertyMap<T>> maps){}  // and you can do     var maps = new List<IGeneralPropertyMap<Object>> {         new GeneralPropertyMap<String>(),         new GeneralPropertyMap<Regex>()     };     //And finally pass the array in.     TakeGeneralPropertyMap<Object>(maps); 

The caveat is that you can't use covariance with value types, so adding a new GeneralPropertyMap<int>() to our list fails at compile time.

cannot convert from 'GeneralPropertyMap<int>' to 'IGeneralPropertyMap<object>' 

This approach may be more convenient than having a non-generic version of your classes/interfaces in case you want to constrain the types that GeneralPropertyMap can contain. In that case:

public interface IMyType {} public class A : IMyType {} public class B : IMyType {} public class C : IMyType {}  public interface IGeneralPropertyMap<out T> where T : IMyType {}  

allows you to have:

var maps = new List<IGeneralPropertyMap<IMyType>> {     new GeneralPropertyMap<A>(),     new GeneralPropertyMap<B>() ,     new GeneralPropertyMap<C>()  }; TakeGeneralPropertyMap(maps); 
like image 26
Paolo Falabella Avatar answered Sep 19 '22 13:09

Paolo Falabella