C# - 有类似 boost::any 的东西吗?
我有一个要求: 1.我需要在列表中存储任何类型的对象 2. 尽可能避免强制调用
为此,我尝试想出一些办法。无论我如何尝试,我都无法摆脱装箱\拆箱。我想知道你们中是否有人遇到过可以实现这一目标的东西。
我创建的类基本上没什么用,除非你处理的是小集合,因为就内存和性能而言,它需要 ArrayList 的 1.5 倍。我正在尝试寻找方法来改进至少其中之一(最好是性能)。
如有任何反馈,我们将不胜感激。
public class Castable
{
Object _o;
public override bool Equals(object obj) { return base.Equals(obj); }
public override int GetHashCode() { return base.GetHashCode(); }
public bool Equals<T>(T obj)
{
T v1 = (T)this._o;
//T v2 = obj;
//var v2 = obj; // Convert.ChangeType(obj, obj.GetType());
// This doesn't work.. (Cannot convert T to Castable
//var v2 = Convert.ChangeType(this.GetType() == obj.GetType() ?
//((Castable)obj)._o.GetType(), obj.GetType());
//if (((T)this._o) != obj) //<== why this doesn't work?
//if (v1 == obj) //<== "Operator '==' cannot be applied to operands of type 'T' and 'T'"
if(v1.Equals(obj))
{
return true;
}
return false;
}
public bool Equals(Castable obj)
{
var v = Convert.ChangeType(obj._o, obj._o.GetType());
return Equals(v);
}
public static bool operator ==(Castable a, Castable b)
{
return a.Equals(b);
}
public static bool operator !=(Castable a, Castable b)
{
return !a.Equals(b);
}
#region HOW CAN WE USE GENRIC TYPE FOR == and != OPERATOR?
public static bool operator ==(Castable a, object b)
{
return a.Equals(b);
}
public static bool operator !=(Castable a, object b)
{
return !a.Equals(b);
}
#endregion
public void Set<T>(T t) { _o = t; }
public T Get<T>() { return (T)_o; }
public static long TestLookup(IList list, int elements, int lookups)
{
object value;
Stopwatch watch = new Stopwatch();
watch.Start();
for (long index = 0; index < lookups; ++index)
{
value = list[random.Next(0, elements - 1)];
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
public static long TestCompare(IList list, int elements, int lookups)
{
//object value;
bool match;
Stopwatch watch = new Stopwatch();
watch.Start();
for (long index = 0; index < lookups; ++index)
{
match = random.Next() == (int)list[random.Next(0, elements - 1)];
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
public static long TestCompareCastable(IList<Castable> list, int elements, int lookups)
{
//object value;
bool match;
Stopwatch watch = new Stopwatch();
watch.Start();
for (long index = 0; index < lookups; ++index)
{
match = list[random.Next(0, elements - 1)] == random.Next(); //most of the times 1.4 times
//match = list[random.Next(0, elements - 1)].Equals(random.Next()); // may be 1.3 times ArrayList
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
public static void Test(int elements, int lookups, int times)
{
List<int> intList = new List<int>();
List<Castable> castableList = new List<Castable>();
ArrayList intArrayList = new ArrayList();
if (Stopwatch.IsHighResolution)
Console.WriteLine("We have a high resolution timer available");
long frequency = Stopwatch.Frequency;
Console.WriteLine(" Timer frequency in ticks per second = {0}", frequency);
for (int index = 0; index < elements; ++index)
{
intList.Add(random.Next());
intArrayList.Add(random.Next());
Castable c = new Castable();
c.Set(random.Next());
castableList.Add(c);
}
long ms = 0;
string result = "";
string ratios = "";
for (int time = 0; time < times; ++time)
{
ms = TestLookup(intList, elements, lookups);
result += "intList Lookup Time " + ms.ToString() + " MS\n";
ms = TestLookup(castableList, elements, lookups);
result += "intArrayList Lookup Time " + ms.ToString() + " MS\n";
ms = TestLookup(intArrayList, elements, lookups);
result += "castableList Lookup Time " + ms.ToString() + " MS\n";
ms = TestCompare(intList, elements, lookups);
result += "intList Compare Time " + ms.ToString() + " MS\n";
long msarraylist = ms = TestCompare(intArrayList, elements, lookups);
result += "intArrayList Compare Time " + ms.ToString() + " MS\n";
ms = TestCompareCastable(castableList, elements, lookups);
result += "castableList Compare Time " + ms.ToString() + " MS\n";
ratios += String.Format("round: {0}, ratio: {1}\n", time, (float)ms / msarraylist);
}
//MessageBox.Show(result);
MessageBox.Show(ratios);
int i = 10;
Castable o1 = new Castable();
o1.Set(i);
int j = 10;
Castable o2 = new Castable();
o2.Set(j);
if (!o1.Equals(10))
{
Console.WriteLine("unequal");
}
if (!o1.Equals(o2))
{
Console.WriteLine("unequal");
}
if (o1 != j)
{
Console.WriteLine("unequal");
}
int x = o1.Get<int>();
}
}
编辑
简而言之,我正在努力实现:
@winSharp93:是的,简而言之: List GenericGenericCollection = new List();
GenericGenericCollection.Add(new string("十四行诗");
GenericGenericCollection.Add(42);
GenericGenericCollection.Add(new MyOwnCustomType);
再次编辑
我发现有两种方法: 1. 在.NET 4中,引入了新的“dynamic”关键字。如果将行 Object _o;
替换为 dynamic _o;
,则可以按原样使用代码。问题是,虽然动态应该是动态类型,但性能就像拳击一样。
可以通过添加隐式(我更喜欢)或显式转换运算符而不是依赖泛型 = 来提高性能= 运算符。
基于http:// /igoro.com/archive/fun-with-c-generics-down-casting-to-a-generic-type/ 我添加了以下课程。这会考虑装箱和性能 - 以下类的性能比 int 或 Castable 的 ArrayList 好一点。当然相比
List
还有很长的路要走。 从我的角度来看,唯一的问题是,一旦将对象分配给普通 Any 对象,即可将具体类型嵌入AnyInternal
中。我也找不到使用方法T Get()
的方法。即使关键字动态在语句运行时也会失败:
Any.AnyInternal
//too bad I can't seal Any after AnyInternal<T> has derived from it.
public abstract class Any
{
public static implicit operator int(Any any)
{
return Any.ToType<int>(any).Data;
}
public static AnyInternal<T> ToType<T>(Any any)
{
return ((AnyInternal<T>)any);
}
public class AnyInternal<T> : Any
{
private T _data;
public T Data { get { return _data; } }
public AnyInternal(T data)
{
_data = data;
}
}
}
I have a requirement where:
1. I need to store objects of any type in list
2. Avoid casting calls as much as possible
To that end I tried to come up with something. No matter what I tried I could not get rid of boxing\unboxing. I wanted to know whether any of you have come across something that will achieve it.
The class I have created is mostly useless unless you are dealing with small collections because in terms of memory and performance it takes 1.5 times ArrayList. I am trying to find ways to improve at least one of them as well (preferably performance).
Any feedback is appreciated.
public class Castable
{
Object _o;
public override bool Equals(object obj) { return base.Equals(obj); }
public override int GetHashCode() { return base.GetHashCode(); }
public bool Equals<T>(T obj)
{
T v1 = (T)this._o;
//T v2 = obj;
//var v2 = obj; // Convert.ChangeType(obj, obj.GetType());
// This doesn't work.. (Cannot convert T to Castable
//var v2 = Convert.ChangeType(this.GetType() == obj.GetType() ?
//((Castable)obj)._o.GetType(), obj.GetType());
//if (((T)this._o) != obj) //<== why this doesn't work?
//if (v1 == obj) //<== "Operator '==' cannot be applied to operands of type 'T' and 'T'"
if(v1.Equals(obj))
{
return true;
}
return false;
}
public bool Equals(Castable obj)
{
var v = Convert.ChangeType(obj._o, obj._o.GetType());
return Equals(v);
}
public static bool operator ==(Castable a, Castable b)
{
return a.Equals(b);
}
public static bool operator !=(Castable a, Castable b)
{
return !a.Equals(b);
}
#region HOW CAN WE USE GENRIC TYPE FOR == and != OPERATOR?
public static bool operator ==(Castable a, object b)
{
return a.Equals(b);
}
public static bool operator !=(Castable a, object b)
{
return !a.Equals(b);
}
#endregion
public void Set<T>(T t) { _o = t; }
public T Get<T>() { return (T)_o; }
public static long TestLookup(IList list, int elements, int lookups)
{
object value;
Stopwatch watch = new Stopwatch();
watch.Start();
for (long index = 0; index < lookups; ++index)
{
value = list[random.Next(0, elements - 1)];
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
public static long TestCompare(IList list, int elements, int lookups)
{
//object value;
bool match;
Stopwatch watch = new Stopwatch();
watch.Start();
for (long index = 0; index < lookups; ++index)
{
match = random.Next() == (int)list[random.Next(0, elements - 1)];
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
public static long TestCompareCastable(IList<Castable> list, int elements, int lookups)
{
//object value;
bool match;
Stopwatch watch = new Stopwatch();
watch.Start();
for (long index = 0; index < lookups; ++index)
{
match = list[random.Next(0, elements - 1)] == random.Next(); //most of the times 1.4 times
//match = list[random.Next(0, elements - 1)].Equals(random.Next()); // may be 1.3 times ArrayList
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
public static void Test(int elements, int lookups, int times)
{
List<int> intList = new List<int>();
List<Castable> castableList = new List<Castable>();
ArrayList intArrayList = new ArrayList();
if (Stopwatch.IsHighResolution)
Console.WriteLine("We have a high resolution timer available");
long frequency = Stopwatch.Frequency;
Console.WriteLine(" Timer frequency in ticks per second = {0}", frequency);
for (int index = 0; index < elements; ++index)
{
intList.Add(random.Next());
intArrayList.Add(random.Next());
Castable c = new Castable();
c.Set(random.Next());
castableList.Add(c);
}
long ms = 0;
string result = "";
string ratios = "";
for (int time = 0; time < times; ++time)
{
ms = TestLookup(intList, elements, lookups);
result += "intList Lookup Time " + ms.ToString() + " MS\n";
ms = TestLookup(castableList, elements, lookups);
result += "intArrayList Lookup Time " + ms.ToString() + " MS\n";
ms = TestLookup(intArrayList, elements, lookups);
result += "castableList Lookup Time " + ms.ToString() + " MS\n";
ms = TestCompare(intList, elements, lookups);
result += "intList Compare Time " + ms.ToString() + " MS\n";
long msarraylist = ms = TestCompare(intArrayList, elements, lookups);
result += "intArrayList Compare Time " + ms.ToString() + " MS\n";
ms = TestCompareCastable(castableList, elements, lookups);
result += "castableList Compare Time " + ms.ToString() + " MS\n";
ratios += String.Format("round: {0}, ratio: {1}\n", time, (float)ms / msarraylist);
}
//MessageBox.Show(result);
MessageBox.Show(ratios);
int i = 10;
Castable o1 = new Castable();
o1.Set(i);
int j = 10;
Castable o2 = new Castable();
o2.Set(j);
if (!o1.Equals(10))
{
Console.WriteLine("unequal");
}
if (!o1.Equals(o2))
{
Console.WriteLine("unequal");
}
if (o1 != j)
{
Console.WriteLine("unequal");
}
int x = o1.Get<int>();
}
}
EDIT
In short I am trying to achieve:
@winSharp93: yes, in short:
List GenericGenericCollection = new List ();
GenericGenericCollection.Add(new string("a sonnet");
GenericGenericCollection.Add(42);
GenericGenericCollection.Add(new MyOwnCustomType);
EDIT AGAIN
There are two ways I found:
1. In .NET 4, a new 'dynamic' keyword is introduced. If you replace the line Object _o;
with dynamic _o;
you can use the code as it is. The problem is although dynamic supposed to be dynamic type, performance is just like boxing..
The performance can be improved by adding implicit (I prefer) or explicit casting operator instead of relying on generic == operator.
Based on http://igoro.com/archive/fun-with-c-generics-down-casting-to-a-generic-type/ I added following class. This takes care of boxing and performance - with following class performance is little better than ArrayList of int or Castable. Of course it has long way to go when
List<int>
compared.
The only problem, from my point of view is, once object is assigned to plain Any object to get concrete type embedded insideAnyInternal<T>
. Neither I could find a way to have methodT Get()
. Even keyword dynamic fails at runtime at statment:
Any.AnyInternal<dynamic> any = (Any.AnyInternal<dynamic>)anyInstanceContainingAnyInternalForInt;
//too bad I can't seal Any after AnyInternal<T> has derived from it.
public abstract class Any
{
public static implicit operator int(Any any)
{
return Any.ToType<int>(any).Data;
}
public static AnyInternal<T> ToType<T>(Any any)
{
return ((AnyInternal<T>)any);
}
public class AnyInternal<T> : Any
{
private T _data;
public T Data { get { return _data; } }
public AnyInternal(T data)
{
_data = data;
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
使用通用 List(在 System.Collections.Generic 内)而不是 ArrayList。
值类型不会发生任何装箱/拆箱。
Use the generic List<T> (inside System.Collections.Generic) instead of ArrayList.
There won't happen any boxing / unboxing for value types.