在Java中,Class有一个通用参数X很方便。但是C#的Type类没有这个。
所以在C#中,如何处理以下Java代码?:
public <X> X methodThatReturns(Class<X> clazz) { ... }
在C#中似乎没有办法连接返回值和传递的Type。
澄清
有几个答案表明方法参数不是必需的,因为该方法可以简单地定义为methodThatReturns<X>()
。
但是如果做有一些未知的Type变量t
,那么基本上没办法调用这样的泛型方法,以便返回类型为t
的对象?
在Java中,您可以自由地传递Class<X>
个变量而不会丢失类型信息,但是在C#中,如果传递等效的Type
变量,则可能会遇到限制,因为当你需要调用泛型方法时你不能使用它们。
答案 0 :(得分:10)
public X methodThatReturns<X>(Class<X> clazz) { ... }
还要记住,C#中的没有类型擦除所以你可以做typeof(T)
这样的事情而不用担心,如果Class是java“Class”对象而不是一个“某类”占位符:
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.
}
答案 1 :(得分:3)
在C#中它将是
public X methodThatReturns<X>() { ... }
您可以使用typeof(X)
而不是使用参数clazz
来获取X的类型
答案 2 :(得分:1)
我不是百分之百地使用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();
答案 3 :(得分:0)
我熟悉Java和.NET泛型,并且在我的Java项目中使用了Class<X>
构造。我最喜欢的用途之一是处理Java中的非泛型集合,这些集合是从外部代码(如Hibernate)返回的(我认为最新版本的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>()
抓取lst中属于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 Expression 。]
Cast<int>()
不同, OfType<int>()
实际上尝试将每个项目转换为int,跳过任何无法转换的项目。 Select()
只允许您使用几乎任何类型的代码从现有集合构建一个全新的集合。
我希望我的真实世界示例可以帮助人们在使用泛型类型的函数时更好地理解.NET和Java之间的差异。