什么是&#34;类型&#34;通用IList <t>?

时间:2016-03-27 03:03:58

标签: c# list generics

想象一下像这样的扩展..

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

想象一下,你想要记下最近被称为的那个。

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

你实际上不能这样做:

  

错误CS0266:无法将System.Collections.Generic.IList<T>类型隐式转换为System.Collections.IList

1-您可以简单地将recent设为object,这似乎工作正常,但这似乎是一个糟糕的解决方案。

2-看来如果你最近有一个IList,你可以实际投出&#34; ra&#34;到那......

recent = (System.Collections.IList)ra;

它似乎有效。看起来很奇怪??所以,

3-实际上,最近 的类型是什么,这样你就不必投它了?如何使recentra的类型相同?你不能这样说....

private static System.Collections.Generic.IList recent;

它没有意义。那么&#34; ra&#34;? recent&#34;&#34;应该是什么?这样你就可以简单地说recent=ra

(我提到这是针对Unity的,因为你经常在Unity中使用通用扩展。)

4-如果你想要一个全部的词典,请考虑另一个困难。

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

我真的只能看到如何将它作为一个对象。

使用案例。

这是您在游戏工程中随处使用的扩展程序,

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];
    }
到目前为止没问题。所以,

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

等等。然而。当然,实际随机选择感觉不好;在实践中你想要的是一个相当不重复的顺序,更像是一个shuffle 。通常,对任何列表或数组做最好的事情是将它们洗牌,然后按顺序提供它们;或许每次都会再次洗牌。简单的例子,你有20个随机音效roars,适用于恐龙咆哮的时候。每次你需要一个,如果你这样做

 roars.AnyOne();
好的,但不是很好。听起来有些糟糕。 (大多数玩家会将其报告为&#34;不是随机的&#34;或者&#34;重复很多&#34;。)这个

 roars.NextOne();

好多了。所以,NextOne()本身应该是(a)如果我们在开始时洗牌,(b)按顺序服务,(c)每次用完时可能再次洗牌列表。 {还有一些细微之处,例如,尽量不要在重新洗牌的结束/开始时重复任何细节,但这里无关紧要。}

请注意,子类化List(和/或数组)会因为许多显而易见的原因而变得很糟糕,它是一个简单的自包含扩展的工作。

那么,这是使用简单的有状态扩展来实现NextOne()的一种很好的方式。

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;
    }

这肯定是有状态扩展的完美范例&#34;。

请注意,我刚刚使用了&#34; object&#34;。

我想在某种程度上,本质量保证中的基本问题是,最好使用&#34;对象&#34;在那里,或者,还有其他更好的东西会更好吗?真的,这就是眼前的问题。干杯!

3 个答案:

答案 0 :(得分:2)

最简单,最安全的解决方案是为每个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;
}

答案 1 :(得分:2)

如果您想要一个全球最新的IList<T>T每次object可能会有所不同,那么您唯一的选择就是使用dynamicIList<T>。两者都需要铸造;后者只是自动投射。

我认为你的困惑源于认为IList继承public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable - it doesn't

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

所以可以说你可以这样做,虽然我没有看到任何优势:

public static void main(String[] args) {
    ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> out = new ArrayList<Integer>(); 
    for(int i =1;i<=2;i++) {
        out.add(i);
        result.add(out);
    }

    System.out.println(result.toString());
}

答案 2 :(得分:2)

  

通用IList的“类型”是什么? T&gt;?

基础类型..

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

System.Object的

通用类型定义......

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

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

并展开SLAKS Answer

  

不是真的。在没有单独的通用非通用基类

的情况下

您也可以使用界面。所以你可以......

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; }
}

然后你可以

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

如果您在列表中添加DogPerson,则无关紧要。

我不敢相信我昨晚没有想到这件事; LINQ使用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);
        }
    }
}

结果:

  

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

     

1

     

2

     

3

     

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

     

乔治

     

亚伯拉罕

尝试:

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;
    }
}