2016-03-27 1 views
2

Imaginez une extension comme ça ..Quel est le "type" d'un IList générique <T>?

public static Blah<T>(this IList<T> ra) 
{ 
.. 
} 

Imaginez que vous voulez faire une note des plus récemment appelé un.

private static IList recent; 
public static Blah<T>(this IList<T> ra) 
{ 
recent = ra; 
.. 
} 

Vous pouvez réellement pas faire:

erreur CS0266: ne peut pas convertir implicitement le type System.Collections.Generic.IList<T> à System.Collections.IList.

1- Vous pouvez simplement faire un recentobject et qui semble fonctionner très bien, mais il semble comme une mauvaise solution.

2- Il semble si vous avez récemment en tant IList, vous pouvez réellement lancer le « ra » que ...

recent = (System.Collections.IList)ra; 

et il semble fonctionner. Cela semble étrange cependant? Ainsi,

3- En fait, quel type devrait être récent de sorte que vous ne devez pas jeter à elle ?? Comment pouvez-vous faire recent le même type que ra? Vous ne pouvez pas dire cela ...

private static System.Collections.Generic.IList recent; 

ce n'est pas significatif. Alors, quel est le type de "ra"? Que devrait être recent "être" de sorte que vous pouvez simplement dire recent=ra?

(Je mentionne cela est pour l'unité, puisque vous utilisez constamment des extensions génériques dans l'unité.)


4- Tenir compte d'une difficulté supplémentaire le cas si vous voulez avoir un dictionnaire de tous.

private static Dictionary<object,int> recents = new Dictionary<object,int>(); 

Je peux vraiment seulement voir comment le faire comme un objet.


EXEMPLE DE CAS D'UTILISATION.

est ici une extension que vous utilisez en permanence, partout, dans l'ingénierie de jeu,

public static T AnyOne<T>(this IList<T> ra) 
    { 
    int k = ra.Count; 
    if (k<=0) {Debug.Log("Warn!"+k);} 
    int r = UnityEngine.Random.Range(0,k); 
    return ra[r]; 
    } 

aucun problème jusqu'à présent. Ainsi,

explosions.AnyOne(); 
yetAnotherEnemyToDefeat = dinosaurStyles.AnyOne(); 

et ainsi de suite. Toutefois. Bien sûr, les sélections aléatoires réelles se sentent mal; en pratique, ce que vous voulez est un ordre assez non-répétitif, plus comme un shuffle. Habituellement, la meilleure chose à faire avec une liste ou un tableau est de les mélanger et de les servir dans cet ordre; peut-être mélanger à nouveau à chaque fois. Exemple simple, vous avez 20 effets sonores aléatoires roars, étant pour quand le dino rugit.Chaque fois que vous en avez besoin, si vous le faites

roars.AnyOne(); 

c'est OK, mais pas génial. Ça va sembler un peu nul. (La plupart des joueurs le signaleront comme "n'étant pas aléatoire" ou "répétant beaucoup".) Ceci

roars.NextOne(); 

est beaucoup mieux. Donc, NextOne() devrait, en soi, (a) si nous sommes au début, mélangez la liste, (b) servez-la dans cet ordre, (c) peut-être remuez-la à nouveau chaque fois que vous utilisez la liste. {Il y a d'autres subtilités, par exemple, essayez de ne pas répéter la fin ou le début du remaniement, mais pas pertinent ici.}

Notez que sous-classer Liste (et/ou tableau) serait nul pour de nombreuses raisons évidentes, c'est un travail pour une extension autonome simple. Alors, voici une belle façon de mettre en œuvre NextOne() en utilisant une simple extension avec état.

private static Dictionary<object,int> nextOne = new Dictionary<object,int>(); 
public static T NextOne<T>(this IList<T> ra) 
    { 
    if (! nextOne.ContainsKey(ra)) 
     // i.e., we've never heard about this "ra" before 
     nextOne.Add(ra,0); 

    int index = nextOne[ra]; 

    // time to shuffle? 
    if (index==0) 
     { 
     Debug.Log("shuffling!"); // be careful to mutate, don't change the ra! 
     IList<T> temp = ra.OrderBy(r => UnityEngine.Random.value).ToList(); 
     ra.Clear(); foreach(T t in temp) ra.Add(t); 
     } 

    T result = ra[index]; 
    ++index; 
    index=index%ra.Count; 
    nextOne[ra] = index; 
    return result; 
    } 

Ceci est sûrement l'exemple parfait d'une "extension avec état".

Remarque en effet, je viens d'utiliser "objet". En un sens, la question fondamentale dans ce QA est: est-il préférable d'utiliser le Dictionary of "object", ou est-ce que quelque chose de plus typé serait mieux? Vraiment c'est la question à portée de main. À votre santé!

+1

Avoir une méthode statique qui maintient l'état statique semble vraiment mauvais pour moi. Je chercherais d'autres solutions qui n'impliquent pas cela. –

+1

'IList ' and'IList 'ne sont pas du même type, donc vous ne pouvez pas avoir une seule variable qui est les deux, puisqu'une variable n'a qu'un seul type. –

+0

Salut Jeff - vous dites que vous n'aimez pas l'idée d'extensions qui sont stateful? – Fattie

Répondre

2

Si vous voulez un IList<T> globalement la plus récente où T varie potentiellement à chaque fois, alors votre seule les options doivent utiliser object ou dynamic. Les deux nécessitent un casting; ce dernier jette juste automatiquement.

Je pense que votre confusion vient de penser que IList<T> hérite IList-it doesn't:

public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable 

donc sans doute vous pourrait ce faire, bien que je ne vois aucun avantage vraiment:

private static IEnumerable recent; 
public static void Blah<T>(this IList<T> ra) 
{ 
    recent = ra; 
    ... 
} 
+0

Merci, j'ai compris; Je suppose que je me bats pour voir pourquoi je ne peux pas faire ceci 'System.Collections.Generic.IList recent'. Après tout, "ra" est un 'System.Collections.Generic.IList' Pourquoi ne puis-je pas simplement faire une variable qui est un 'System.Collections.Generic.IList recent' ... Je suppose que je ne comprends pas cela. : O – Fattie

+0

En effet: pensez-vous qu'il y a un problème à simplement lancer ra à '(System.Collections.IList) ra'? difficile ... – Fattie

+1

@JoeBlow Juste ajouté un peu plus pour clarifier. –

2

Le plus simple, et la plupart de type sécurisé, la solution est de stocker une valeur distincte pour chaque T:

private static class RecentHolder<T> { 
    public static IList<T> Value { get; set; } 
} 
public static Blah<T>(this IList<T> ra) { 
    RecentHolder<T>.Value = ra; 
} 
+0

Mais alors il y aura un "plus récent" par type, pas globalement. –

+0

Aussi, je veux vraiment connaître la réponse à ma question (3) - heh! De quel type d'enfer est-ce? Il doit y avoir un type où vous pouvez dire 'x = ra;' (autre que simplement 'object'.) – Fattie

+0

@JoeBlow: Pas vraiment. En l'absence d'une classe de base commune non générique séparée, le seul type commun entre «IBlah » et «IBlah » est 'object'. OTOH, 'IList ' arrive à implémenter le non-générique 'IEnumerable', donc il y a cela. S'il avait été covariant (par exemple, 'IReadOnlyList '), il y aurait aussi 'IReadOnlyList '. – SLaks

2

Quel est le "type" d'un IList générique < T>?

Le type de base ..

Console.WriteLine(new List<int>().GetType().BaseType); 

System.Object

La définition de type générique ...

Console.WriteLine(new List<int>().GetType().GetGenericTypeDefinition()); 

System.Collections.Generic .List`1 [T]

Et pour développer SLAKS Answer

Pas vraiment. En l'absence d'une classe de base commune non générique distincte

Vous pouvez également utiliser des interfaces. Ainsi, vous pouvez faire ...

public interface IName 
{ 
    string Name { get; set; } 
} 
public class Person : IName 
{ 
    public string Name { get; set; } 
} 
public class Dog : IName 
{ 
    public string Name { get; set; } 
} 

Ensuite, vous pouvez

private static List<IName> recent; 
public static Blah<T>(this List<IName> ra) 
{ 
    recent = ra; 
    .. 
} 

et il ne sera pas si vous mettez Dog ou Person dans la liste.

OU

Je ne peux pas croire que je ne pensais pas à cette dernière nuit; LINQ à la rescousse en utilisant object.

using System; 
using System.Linq; 
using System.Collections.Generic;     

public class Program 
{ 
    private static class WonkyCache 
    { 
     private static List<object> cache = new List<object>(); 

     public static void Add(object myItem) 
     { 
      cache.Add(myItem); 
     } 

     public static IEnumerable<T> Get<T>() 
     { 
      var result = cache.OfType<T>().ToList(); 
      return result; 
     } 
    } 

    public static void Main() 
    { 
     WonkyCache.Add(1); 
     WonkyCache.Add(2); 
     WonkyCache.Add(3); 
     WonkyCache.Add(Guid.NewGuid()); 
     WonkyCache.Add("George"); 
     WonkyCache.Add("Abraham"); 

     var numbers = WonkyCache.Get<int>(); 
     Console.WriteLine(numbers.GetType());   

     foreach(var number in numbers) 
     { 
      Console.WriteLine(number); 
     } 

     var strings = WonkyCache.Get<string>(); 
     Console.WriteLine(strings.GetType()); 

     foreach(var s in strings) 
     { 
      Console.WriteLine(s); 
     } 
    } 
} 

Résultats:

System.Collections.Generic.List`1 [System.Int32]

système. Collections.Generic.List`1 [System.String]

George

Abraham

Essayez:

public static class StatefulRandomizer<T> 
    // Use IEquatable<T> for Intersect() 
    where T : IEquatable<T> 
{ 
    // this could be enhanced to be a percentage 
    // of elements instead of hardcoded 
    private static Stack<T> _memory = new Stack<T>(); 
    private static IEnumerable<T> _cache; 

    public static void UpdateWith(IEnumerable<T> newCache) 
    { 
     _cache = newCache.ToList(); 

     // Setup the stack again, keep only ones that match 
     var matching = _memory.Intersect(newCache); 
     _memory = new Stack<T>(matching); 
    } 

    public static T GetNextNonRepeatingRandom() 
    { 
     var nonrepeaters = _cache 
      .Except(_memory); 

     // Not familar with unity.. but this should make 
     // sense what I am doing 
     var next = nonrepeaters.ElementAt(UnityEngine.Random(0, nonrepeaters.Count()-1)); 

     // this fast, Stack will know it's count so no GetEnumerator() 
     // and _cache List is the same (Count() will call List.Count) 
     if (_memory.Count > _cache.Count()/2) 
     { 
      _memory.Pop(); 
     } 

     _memory.Push(next); 

     return next; 
    } 
} 
+0

Un excellent point sur 'interface'. Awesome – Fattie

+0

Maintenant que mon cerveau est clair, la solution supplémentaire au fond pourrait être d'une grande utilité. –

+0

J'ai lu votre exemple, et ce que vous essayez en fait vraiment un [XY Problem] (http://meta.stackexchange.com/a/66378/171858). Il semble que ce que vous essayez de faire est de prendre une liste de * connu * T, je dis bien connu, parce que le consommateur sait qu'ils sont * rugit * obtenir un un "aléatoire". SLaks réponse dans ce cas vraiment résout le problème, je vais juste ajouter la partie aléatoire dans une mise à jour ... –