【问题标题】:C# Generics: How can I use them generically?C# 泛型:如何通用地使用它们?
【发布时间】:2010-12-22 19:56:48
【问题描述】:
[TestMethod]
public void TestMyGenericBaseClasses()
{ 
    Type typeCrazy = ThisPartyIsTypeCrazyWOOT();

    // How do I create a generic object?
    MadnessOhYeah<typeCrazy> sanity = new MadnessOhYeah<typeCrazy>();

    // How do I use a generic object after it has been created?
    Assert.IsTrue(sanity.MyTrueFunction(), "this is sparta");

    // How do I call a generic function generically?
    bool result = MyFunction<typeCrazy>();

    Assert.IsTrue(result, "I did not get my teeth whitened!");

}

有什么方法可以编译吗? (ThisPartyIsTypeCrazyWOOT 返回一个类型)因为这是一个测试,所以我们不担心必须使用反射或任何东西,除非那绝对是疯狂的。

我感觉这是不可能的,我们的测试功能必须更加具体。

【问题讨论】:

  • 什么是编译错误?我怀疑您需要向 MadnessOhYeak 类添加一个约束,即泛型类型是“newable”。

标签: c# generics


【解决方案1】:

【讨论】:

  • 我看不到用这个方法创建的对象之后我会如何使用它。
【解决方案2】:

更新 2:我发布的第一个示例仍然没有 100% 回答问题,因为它涉及到 List&lt;int&gt; 的强制转换,这是在编译时已知的类型。下面是一个仅反射的解决方案,说明您如何可以使用对类型参数本身一无所知的泛型类型。但是,正如你所看到的,它......好吧,恶心;)

Type userType = GetUserSuppliedType();

// Now let's say userType is T.
// Then here we are getting the type typeof(List<T>).
// But, of course, there's no way to have any such information in the code.
Type listOfUserType = typeof(List<>).MakeGenericType(new[] { userType });

// This is effectively calling new List<T>();
object listObject = Activator.CreateInstance(listOfUserType);

// Do you see how messy this is getting?
MethodInfo addMethod = listOfUserType.GetMethod("Add");

// We better hope this matches userType!
object input = GetUserSuppliedInput();

// I suppose we could check it, to be sure...
if (input == null || input.GetType() != userType)
{
    throw new InvalidOperationException("That isn't going to work!");
}

// Here we are finally calling List<T>.Add(input) -- just in the most ass-
// backwards way imaginable.
addMethod.Invoke(listObject, new[] { input });

更新:好的,如果您坚持这样做,这里有一个例子说明它是如何可能的——但非常麻烦!

Type genericListType = typeof(List<>);
Type listOfInt32Type = genericListType.MakeGenericType(new[] { typeof(int) });

object listObject = Activator.CreateInstance(listOfInt32Type);

List<int> list = (List<int>)listObject;

list.Add(1);

泛型不能像这样工作,因为Type 对象可以是任何东西。考虑这段代码:

var list = new List<int>();
list.Add(1);

上面代码中list的类型已知为List&lt;int&gt;,它定义了list上哪些操作是合法的,比如Add(1)

现在考虑一下:

Type t = GetTypeFromIndeterminateSourceSuchAsUserInput();
var list = new List<t>();
list.Add(?);

tType 对象而不是编译器可以解析的类型(如int)的名称 时,实际上不可能使用它来实例化泛型类型类型——或者更确切地说,它是可能的(参见Andrey's answer),但你不能真正使用以任何通用方式生成的对象。

现在,您可能认为这样的事情应该可以工作:

Type t = typeof(int);
var list = new List<t>();
list.Add(1);

...但仅仅因为 t 的值在编译时(由您)知道并不会改变一般的工作方式。

无论如何,是的,可以使用反射;但如果你沿着这条路走下去,你就会致力于一个非常注重反思的解决方案。我的意思是,总的来说,这并不是一件特别现实的事情。

【讨论】:

  • 你有没有用反射来做这件事的例子?我修改了我的问题以使其更好。
  • @thepaulpage:我添加了一个示例。如您所见,它真的不漂亮。但也许它可以以某种方式帮助你。
  • 嗯...我明白你在做什么,但我们确实需要使用我们创建的对象(同时仍然不知道类型)。虽然您的示例没有这样做,但它已尽其所能。我想这意味着答案是否定的。
  • @thepaulpage:我发布了第二个示例,说明从技术上讲,您如何真正在代码中使用泛型类型,同时保持完全忽略它类型参数。但是,正如您希望同意的那样,这种方法非常令人震惊。无论如何,我会让你自己判断。
  • 非常感谢您提供的完整示例。你说得对,它很可怕,但由于它只是一种测试方法,我们还是可以调查一下。目前我正在尝试在 T4 中做我们需要做的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-17
  • 1970-01-01
  • 2010-09-09
  • 2013-06-12
相关资源
最近更新 更多