c#中与Java' Class类型
本文关键字:类型 Class 中与 Java | 更新日期: 2023-09-27 17:50:31
在Java中,class有泛型形参x很方便,但是c#的Type类没有这个。
那么在c#中,如何完成下面的Java代码呢?:
public <X> X methodThatReturns(Class<X> clazz) { ... }
在c#中似乎没有办法将返回值和传递的Type连接起来。
有几个答案表明方法参数是不必要的,因为方法可以简单地定义为methodThatReturns<X>()
。
但是如果确实有一些未知的类型变量t
,基本上没有办法调用这样一个泛型方法,这样它将返回一个类型为t
的对象?
在Java中,您可以自由地传递Class<X>
变量而不会丢失类型信息,但似乎在c#中,如果您传递等效的Type
变量,您可能会遇到限制,因为当您需要调用泛型方法时无法使用它们。
public X methodThatReturns<X>(Class<X> clazz) { ... }
还请记住,在c#中没有类型擦除,所以您可以做像typeof(T)
这样的事情而不用担心,如果Class意味着是java的"类"对象而不是"某些类"占位符:
public X methodThatReturns<X>(X value)
{
Type x = typeof(X); // that's fine
if (value is SomeType) { } // that's fine too
return (X)someObject; // I think you get the point
}
编辑:同样,由于泛型类型信息在编译后不会丢失,因此不需要显式地传递类型:
public X methodThatReturns<X>()
{
Type xType = typeof(X); // Type is to C#/.Net what Class<X> is to Java.
}
在c#中是
public X methodThatReturns<X>() { ... }
,你可以得到类型的X使用typeof(X)
而不是使用参数clazz
我不是100%的Java泛型…您是否试图声明一个返回与传递的类型相同的方法?
public T MethodThatReturns<T>(T input) { ... }
此方法将是任何类型的泛型,并返回传递给它的相同类型。你可以使用:
AnyTypeAtAll foo = new AnyTypeAtAll();
AnyTypeAtAll bar = MethodThatReturns(foo);
请注意,在调用MethodThatReturns时没有<AnyTypeAtAll>
,编译器可以根据您传递的参数计算出它。但是,请注意,这是编译器在做,而不是运行时,所以它只会使用变量foo的类型,而不是foo指向的对象的类型。
另一方面,如果这是一个不带"真实"参数的方法的Java语法,那么只需:
public T MethodThatReturns<T>()
{
Type clazz = typeof(T);
....
}
在这个方法中,您可以像对待任何其他类一样对待T,并且它将专门引用调用该方法时使用的类型,而不是object
或类似的类型。您可能希望将where T : class
设置为允许在该方法中对null进行比较(这确实阻止您使用int
作为泛型类型),或者将where T : new()
设置为限制泛型类型具有默认构造函数,因此您可以执行T foo = new T();
我熟悉Java和。net泛型,我在Java项目中经常使用Class<X>
结构。我最喜欢的一个用途是处理从Hibernate等外部代码返回的Java中的非泛型集合(我认为Hibernate的最新版本可能支持泛型,尽管我不确定)。我们正在使用一个相当旧的版本,因为它是一个长期运行的项目,有太多的代码,如果我们改变它,就必须更新。)函数是这样工作的:
public <X> List<X> TypedList(List<?> lst, Class<X> cls) {
return (List<X>) lst;
}
这非常简单,重要的是我不需要创建一个虚拟对象来传递给函数,我只需要将其命名为:
List<MyObject> myLst = TypedList(lst, MyObject.class);
另一个需要注意的重要事项是根本没有使用cls参数。这是给出x的具体类型的唯一方法。
在c#中做同样的事情的方式有点不同。您可能会认为相同的函数看起来像这样:
public List<X> TypedList<X>(IList lst)
{
return (List<X>) lst;
}
但是你完全错了。
问题是Java使用了一个叫做Type Erasure的技巧,这基本上意味着当代码实际编译时,所有泛型参数都从代码中删除。因此,在Java虚拟机的世界中,没有List<X>
或任何其他泛型,它们都是简单的List
和其他非泛型类型。这意味着泛型只是在那里帮助你编写代码,而不是在运行时强制执行。
然而,在c#中没有类型擦除,泛型在运行时强制执行。这意味着当与上述函数一起使用时,以下代码将生成运行时异常:
List<object> lstObj = TypedList<object>(new List<string>());
原因是List<string>
和List<object>
被认为是两个完全不同的类。你甚至不能做List<object> lstObj = (List<object>) new List<string>();
而不得到编译器错误。这意味着函数不能返回传递给它的相同对象。必须创建一个正确类型的新对象并返回该对象。最基本的形式如下所示:
public List<X> TypedList<X>(IList lst)
{
List<X> lstOut = new List<X>();
for (int i = 0; i < lst.Count; i++)
{
if (lst[i] is X) lstOut.Add((X) lst[i]);
}
return lstOut;
}
这是相当无聊的样板代码,但它可以工作。对于更短、更简洁的东西,LINQ在这里拯救了一天。查看以下内容:
public List<X> TypedList<X>(IList lst)
{
return lst.OfType<X>().ToList();
}
OfType<X>()
获取list中所有类型为X(或后代)的项,并跳过任何非X类型的项。然后,ToList()
构建一个新的List<X>
,包含从OfType<X>()
传递给它的所有成员。这种方法的另一个优点是,我们实际上可以将lst参数从IList
更改为IEnumberable
,这是由所有集合类型实现的。
另一件需要指出的事情是,它不做任何类型转换,只做类型检查。因此,这意味着如果您想将List<long>
转换为List<int>
或List<string>
,则需要做其他事情。对于LINQ,这仍然非常简单:
List<long> lstLng = new List<long>();
lstLng.Add(1);
List<int> lstInt = lstLng.Cast<int>().ToList();
List<string> lstStr = lstLng.Select(lng => lng.ToString()).ToList();
[注意:如果你不熟悉lng => lng.ToString()
部分,它被称为Lambda表达式]
Cast<int>()
,与OfType<int>()
不同,实际上尝试将每个项转换为int,跳过任何转换失败的项。Select()
只是让你从现有的集合中构建一个全新的集合,使用几乎任何你想要的代码。
我希望我的真实例子可以帮助人们更好地理解。net和Java在使用泛型函数时的区别。