Cómo: Generar tipos genéricos dinámicos

Hace un tiempo, empecé a preguntarme: “Cómo podría crear objetos genéricos específicos como List o Dictionary con tipos enviados como parámetros?”. Todos sabemos que entre “” tenemos que especificar el(los) tipo(s), pero ciertamente no podemos hacer algo como “Dictionary”.

Mi estimado colega Angel “Java” Lopez me dijo una vez que dicha creación dinámica de genéricos se podía hacer (no de esa forma, se refería a que el objetivo se podía alcanzar :P). Así que comencé a investigar y preguntar, hasta que encontré el método Type.MakeGenericType(Type[] args). Leí la documentación y finalmente hice funcionar la idea! Así que lo compartiré con ustedes! :)

Código Ejemplo

Este ejemplo muestra una clase llamada FabricaDeGenericos que genera objetos genéricos de acuerdo con los tipos pasados como parámetros. Para este propósito, la clase expone un método llamado CrearInstanciaGenerica(Type tipoBase, Type[] tiposDeArgumento).

Parámetros:

  • tipoBase: el tipo que será el tipo base genérico, y
  • tiposDeArgumento: un arreglo (array) de argumentos que proveerá los tipos para los argumentos genéricos.

Devuelve:

  • object.

public class FabricaDeGenericos
{
    public object CrearInstanciaGenerica(
        Type tipoBase, Type[] tiposDeArgumento
        )
    {
        return Activator.CreateInstance(tipoBase.MakeGenericType(
            tiposDeArgumento));
    }
}

Esta clase recibe el tipo de la clase base del genérico y sus argumentos genéricos, y luego llama al método MakeGenericType(Type[] args) de la clase System.Type de tipoBase que construye el genérico.

Hora, podemos preparar nuestra clase Cliente (aquí es una Windows Console Application) para que use la clase FabricaDeGenericos de la siguiente manera:

class Program
{
    static void Main(string[] args)
    {
        FabricaDeGenericos miFabricaDeGenericos = new FabricaDeGenericos();

        Type[] misTiposDeArgumento = { typeof(string), typeof(object) };

        Dictionary<string, object> miDiccionario =
            (Dictionary<string, object>)miFabricaDeGenericos.CrearInstanciaGenerica(
                typeof(Dictionary), misTiposDeArgumento);

        Console.WriteLine(miDiccionario.GetType().FullName);
        Console.Read();
    }
}

Aquí, estoy creando un nuevo Dictionary. Si le dan una mirada al código del Cliente, le estoy enviando el objeto tipo base que representa el tipo genérico Dictionary omitiendo los argumentos tipo, pero manteniendo la coma que los separa, para que el compilador pueda inferir el número de parámetros tipo (y, por supuesto, para evitar excepciones :P). Luego, también le envío un arreglo (array) de tipos (string para TKey y object para TValue) para reemplazar los parámetros tipo de Dictionary.

NOTA: La conversión (casting) es necesaria ya que el método CrearInstanciaGenerica() devuelve object! :)

Finalmente, llamo al método MakeGenericType(Type[] args) en el tipoBase el cuál devolverá el tipo para Dictionary. Activator.CreateInstance(Type TType) simplemente genera el objeto que será devuelto de acuerdo con el tipo.

La línea Console.WriteLine() muestra en la consola el tipo construido del nuevo objeto.

Más información:

- Salu2, Nacho

See this topic in English.

When to use "static"?

Contents

  • Introduction.
  • A C++ reference on “static”.
  • “Const” vs. “Static”.
  • C# “static”.
  • Conclusion.
  • References.

Introduction

This article is the outcome of a question that popped up in my mind when I was dealing with Factories and (therefore, together with) Singletons. One of the technologies I’m currently working on is the Composite Application Block (CAB) and the Smart Client Software Factory (SCSF). I mentioned this as in the CAB/SCSF there are functional classes called Services, which are allocated in containers (called WorkItems, but I mention the name just for reference) distributed in a tree-like hierarchy (from a root WorkItem to child WorkItems).

Services are treated liked Singletons: only one instance of a Service is available during the lifetime of a specific WorkItem. Moreover, once a child WorkItem is added (to the parent’s WorkItems collection), all Services are percolated to the Service collection of the child. So, a Service registered in the root is available throughout the whole application until the root WorkItem is terminated (which will occur once the application is ended). However, Services are not static nor follow the Singleton pattern (for a quick glimpse on the Singleton pattern click here)!

The bolded and underlined line is one of the reasons that moved me to start investigating on “static” (the other one, is Factories and its implementation as Singletons, which I’ll discuss in a next post :) ). The reason for this is that each child WorkItem may override a specific Service that is passed (from its parent WorkItem) into its Service collection, so, now, we may have more than one instance of a given Service type throughout the whole application, but there still can be one and only one instance of a Service in a specific WorkItem (I think this is the scope of “Singleton Service” in CAB/SCSF). Besides, Services are not static as they can be added and removed from the WorkItem at any time.

Enough of this introduction! Let’s get into the core subject! :)

A C++ reference on “static”

The static keyword specifies that the variable has static duration (it is allocated when the program begins and deallocated when the program ends) and initializes it to 0 unless another value is specified. So, as a descriptive term it refers to the lifetime of memory or storage locations, which I’ll mention the following types:

  • static
  • dynamic (heap)
  • auto (stack)

Static storage refers to memory locations that persist for the life of the program (i.e.: global variables), while stack storage comes and goes as functions are called (the call stack composed by “stack frames” corresponding to subroutine calls), and heap storage refers to memory locations allocated and deallocated using operators new and delete.

Objects and variables defined outside all blocks have static lifetime and external linkage (its name is visible from files other than the one in which it’s defined) by default, while a global object or variable that is explicitly declared as static has internal linkage (the names of objects and functions refer only to program elements inside their own translation units, they are not shared with other translation units).

Moreover, static can also be used in terms of visibility of objects. For example:

static int _number = 2;

means that _number is not visible outside the source file it was defined. When modifying a variable or function at file scope, the static keyword specifies that the variable or function has internal linkage. However, static meaning “local to a file”  has turned obsolete with the introduction of C++ namespaces, as an unnamed namespace is used to wrap the static declarations (some more info here).

Using C++, it is possible to have static members of a class which are shared across all object instances of that class.

class MyClass {
public:
    static int _id;
    static void sharedFunction();
};

int MyClass::_id = 0;
void MyClass::sharedFunction() {}

When modifying a data member in a class declaration, the static keyword specifies that one copy of the member is shared by all instances of the class (which must be initialized at file scope).

In the example above, once the data member MyClass::_id is set to 0, that value will be shared among all object instances of MyClass. So, in recursive code, a static object or variable is guaranteed to have the same state in different instances of a block of code, which can be useful to share information between object instances.

When modifying a member function in a class declaration, the static keyword specifies that the function can be called even when a class is not instantiated. A static member function cannot be declared with the keywords virtual, const, volatile, or const volatile, and can access only the names of static members, enumerators, and nested types of the class in which it is declared (it does not have a this pointer).

In the example, the function member MyClass::sharedFunction() can be used to provide utility functions to a class. This is done when the function is related to an operation of the class but it doesn’t operate on particular object instances of the class. This is because the function doesn’t (or doesn’t need to) operate on an object’s state.

If the function’s modifier is public, it’s made global, but if it only refers to (or “has sense” inside) the class it has been defined, it’s cleaner/better to have the function as part of its package, as follows:

class MyClass {
static void sharedFunction();

public:
static int _id;
};

Now, MyClass::sharedFunction()is private (of course, it can be added to the private section of the class) to MyClass and can only be used within its member functions, instead of by the whole program.

In addition to this, a variable declared static in a function retains its state between calls to that function:

#include

using namespace std;

class MyClass {
public:
    static int _id; 
    void nonSharedFunction(int);
};

int MyClass::_id = 0;
void MyClass::nonSharedFunction(int parameter) {
    static int sharedNumber = 4;
    sharedNumber += parameter;
    cout << sharedNumber << endl;
}

int main() {
MyClass* firstInstance = new MyClass();
MyClass* secondInstance = new MyClass();

firstInstance->nonSharedFunction(2);
secondInstance->nonSharedFunction(4);

cin.get();
return 0;
}

Output:

6
10

In this example, I changed the static sharedFunction() function to a non static function called nonSharedFunction(int parameter) which accepts an integer as a parameter. The nonSharedFunction(int parameter) contains a static variable (called sharedNumber) which will be shared among all instances of MyClass, so each call to nonSharedFunction(int parameter) will increase the value of sharedNumber.

“Const” vs. “Static”

This section is to briefly distinguish that, although const means static, static does not mean const. Constants and static values are allocated in the same memory section (the static storage) as both persist for the lifetime of the program. However, static values are not constant (unless you explicitly specify so ;) ) as shown in the last example of the A C++ reference on “static” section.

Both, const and static are used to “share” values, but you use const to preserve constant a certain value (such as URIs), while the static “shared” value may vary in time (for example, a data provider).

C# “static”

C# deals with static practically in the same way as C++. The example:

public class MyClass
{
    public static int _id = 0;

    public static void sharedFunction(int parameter) {
        int number = 4;
        number += parameter;
    }
}

Means:

  • The _id data member…
    • …will be allocated when the program begins and deallocated when the program ends.
    • …will be shared by all instances of MyClass.
    • …will be publicly accessible (unless we remove the public modifier).
    • …will be initialized to 0 (unless we specify another value).
  • The sharedFunction(int parameter) function member…
    • …will be allocated when the program begins and deallocated when the program ends.
    • …will be publicly accessible (unless we remove the public modifier) by Type, not by instance.
    • …will have no this pointer.
    • …will only be able to access static data members.
    • …cannot be marked as override, virtual or abstract.

Note that, this time, the function variable sharedNumber is called just number, as we cannot declare static function variables.

Moreover, if we declare a static class, all of its members must be static as well.

Conclusion

Well, let’s get into answering the question: “When to use “static”?”. Some people have the bad tendency of using static to declare “global” values, declaring an object’s state public and static so that they can modify it from wherever they want. This is a terrible practice! This breaks the concepts of encapsulation and, therefore, visibility. It makes the code “dirty” and hard to maintain and debug.

Another “code smell” is to declare a static class with static “homeless” methods. Some people cannot find a “home” for their utility methods and tend to declare a static class to store a whole bunch of (sometimes unrelated!) methods. Some valid static classes with static methods are Factories, as they store creation methods, which do not depend on a state (although some do, for example, those ones who need some kind of configuration for all creations).

In conclusion, static must be used, for example, in data members when we want to share information among all instances of the class that contains them, and in member functions when the function doesn’t need to operate on a particular state (i.e.: creation methods; and stateless methods, but packed in a class in which they “make sense” or are related to).

References

How To: Make dynamic generic types

Some time ago, I started wondering: “How could I create specific generic objects such as List or Dictionary with types sent as parameters?”. We all know that between “” we must provide the type(s), but we cannot certainly do something like “Dictionary”.

My dear workmate Angel “Java” Lopez told me once that such dynamic creation of generics could be done (not in that way, he meant that the objective could be reached :P). So, I started searching and asking, till I found the method Type.MakeGenericType(Type[] args). I read the documentation and finally got the idea to work! So I’ll share this with you! :)

Sample Code

This example shows a GenericsFactory class that populates generic objects according to the types passed as parameters. For this purpose, the class exposes a CreateGenericInstance(Type baseType, Type[] typeArguments) method.

Parameters:

  • baseType: a type that will be the generic base type, and
  • typeArguments: an array of arguments which will provide the types for the generic arguments.

Returns:

  • object.

public class GenericsFactory
{
   public object CreateGenericInstance(
        Type baseType, Type[] typeArguments
        )
    {
        return Activator.CreateInstance(baseType.MakeGenericType(
            typeArguments));
    }
}

This class receives the generic base class type and its generics arguments, and then it calls the MakeGenericType(Type[] args) method of the System.Type class for the baseType which constructs the Generic.

Now, we can set our Client class (here’s a Windows Console Application) to use the GenericsFactory class as follows:

class Program
{
   static void Main(string[] args)
    {
        GenericsFactory myGenericsFactory = new GenericsFactory();

        Type[] myTypeArguments = { typeof(string), typeof(object) };

        Dictionary<string, object> myDictionary =
            (Dictionary<string, object>)myGenericsFactory.CreateGenericInstance(
            typeof(Dictionary), myTypeArguments);

        Console.WriteLine(myDictionary.GetType().FullName);
        Console.Read();
    }
}

Here, I’m creating a new Dictionary. If you have a look at the Client code, I’m sending the base type object representing the generic Dictionary type omitting the type arguments, but keeping the comma that separates them, so that the compiler can infer the number of type parameters (and, of course, to avoid exceptions :P). Then, I also send an array of types (string for TKey and object for TValue) to replace the type parameters of Dictionary.

NOTE: Casting is needed as the CreateGenericInstance() method returns object! :)

Finally, I call the MakeGenericType(Type[] args) method on the baseType which will return the type for Dictionary. Activator.CreateInstance(Type TType) just populates the object that will be returned according to the type.

The Console.WriteLine() line displays in the console the built type of the new object.

More information:

- Cheers, Nacho

Ver este tópico en español.