想象一下像这样的扩展..
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-实际上,最近 的类型是什么,这样你就不必投它了?如何使recent
与ra
的类型相同?你不能这样说....
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;在那里,或者,还有其他更好的东西会更好吗?真的,这就是眼前的问题。干杯!
答案 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
可能会有所不同,那么您唯一的选择就是使用dynamic
或IList<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;
..
}
如果您在列表中添加Dog
或Person
,则无关紧要。
或强>
我不敢相信我昨晚没有想到这件事; 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;
}
}