2012-01-25 3 views
1

Je ne sais pas si cela a déjà été demandé, alors dirigez-moi vers une autre question si c'est le cas.Créer un paramètre de type basé sur une autre valeur

J'ai une méthode comme ceci:

private void SomeMethod<TLocation>(int x, int y) where TLocation : DataLocation 
{ 
    // 
} 

Dans la méthode que je souhaite l'appeler avec, j'ai un ENUM, et je veux appeler la méthode avec le paramètre de type.

public enum LocationType 
{ 
    Country, 
    State, 
    County, 
    City, 
    Neighborhood, 
    Street 
} 

Types de DataLocation:

DataCountry 
DataState 
DataCounty 
DataCity 
DataNeighborhood 
DataStreet 

Sachant que le paramètre de type est "Data" + nom de ENUM, est-il possible que je peux appeler dynamiquement ma méthode?

Ou devrais-je rester avec:

switch (locationType) 
{ 
    case LocationType.Country: 
     SomeMethod<DataCountry>(1, 2); 
     break; 
    case LocationType.State: 
     SomeMethod<DataState>(2, 4); 
     break; 
    // etc 
} 

EDIT:

On dirait que la réflexion est la seule façon. Je vais coller avec l'interrupteur.

+2

En général, cela indique que 'SomeMethod' devrait vraiment être une méthode virtuelle sur un autre type de données. Il est difficile de savoir sans regarder votre hiérarchie réelle, cependant :) – porges

+0

@Porges - ouais, je sais. :) Il s'agit d'une application ASP.NET MVC, et 'SomeMethod' est en fait une méthode spécifique à l'interface utilisateur (en configurant ViewModel), donc elle n'appartient pas au domaine (l'emplacement). – RPM1984

Répondre

1

Voici la solution:

var dispatches = new Dictionary<LocationType, Action<int, int>>(); 
dispatches.Add(LocationType.Country, SomeMethod<DataCountry>); 
dispatches.Add(LocationType.State, SomeMethod<DataState>); 
//... and etc. 

dispatches[LocationType.Country](1, 2); // the same as SomeMethod<DataCountry>(1,2) 
+0

C'est en fait vraiment intelligent. J'essaierai ça quand je serai de retour au bureau. – RPM1984

+0

Oui, j'aime ça, et ça marche. Genre de "adoucit" l'interrupteur. Accepté – RPM1984

0

Je pense que vous devriez vous en tenir à l'implémentation que vous avez suggérée en utilisant l'instruction switch. C'est clair et concis et je ne sais pas comment vous pouvez le faire de façon dynamique.

+0

sûrement il y a un moyen. 'MakeGenericType <>'? – RPM1984

0

Il semble que cette méthode devrait être définie comme membre de la classe DataState, et remplacée en conséquence, éventuellement avec l'utilisation interne de this.GetType(). Vous utilisez en quelque sorte les génériques ici.

Cela dit ... (je l'espère presque je downvoted pour cela ...)

using System; 
using System.Globalization; 
using System.Linq; 
using System.Reflection; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      new ResolveIt<LocationType>().InvokeIt(LocationType.State, 1, 5); 
      Console.ReadLine(); 
     } 
    } 

    public class ResolveIt<TEnum> // Unfortunately can't constrain on enums 
    { 
     private static readonly Action<int, int>[] Actions 
      = Enum.GetValues(typeof(TEnum)) 
       .Cast<TEnum>() 
       .Select(v => typeof(ResolveIt<TEnum>) 
       .GetMethods(BindingFlags.NonPublic | BindingFlags.Static) 
       .First(n => n.Name == "SomeMethod") 
       .GetGenericMethodDefinition() 
       .MakeGenericMethod(new[] { Type.GetType(typeof(ResolveIt<TEnum>).Namespace + ".Data" + Enum.GetName(typeof(TEnum), v)) })) 
       .Select(mi => (Action<int, int>)Delegate.CreateDelegate(typeof(Action<int, int>), mi)) 
       .ToArray(); 

     public void InvokeIt(TEnum type, int x, int y) 
     { 
      Actions[(Int32)Convert.ChangeType(type, typeof(Int32))](x, y); 
     } 

     private static void SomeMethod<TLocation>(int x, int y) where TLocation : DataLocation 
     { 
      Console.Out.WriteLine(typeof(TLocation)); 
     } 
    } 
    public enum LocationType { Country, State, City, Zip, } 
    public class DataLocation { } 
    public class DataCountry:DataLocation { } 
    public class DataState:DataLocation { } 
    public class DataCity:DataLocation { } 
    public class DataZip:DataLocation { } 
} 
+0

voir ma réponse au commentaire de Porges pour voir pourquoi je ne fais pas ça. – RPM1984

+0

Ajouté une belle façon laide de git 'er fait, comme on dit ... –

Questions connexes