如何“存储”一个类型(不是“Type”对象)以备将来使用?

时间:2014-01-07 22:31:38

标签: c# design-patterns types

这很少需要,但有时仍然有用。

假设有阶段。在第一阶段,您可以访问某些类型T,但您现在无法使用它。然后,稍后,执行第二阶段,并且必须执行涉及第一阶段中已知的类型T的事情。 System.Type对象不是解决方案,因为与真实类型相比,它们非常有限。例如。你不能写Type type = typeof(int); new List<type>()

让我们正确化问题:

public static Something Store<T>() {
    //store T and return it
}

public static void Use(Something smth) {
    //do something with T (e.g. create a `List<T>` instance and pass it to Console.WriteLine)
}

我该如何做到这一点?

更新: 一个用例示例:假设我们有两种不同的独立算法。一个选择元素数据类型(int / float / decimal等),另一个选择容器类型(List<> / LinkedList<> / {{1} }/等等)。每种类型都有许多种类,并且不知道整个可能的类型集。现在我们需要编写创建类型化容器的中心代码片段(例如HashSet<>)。我们不能将这些代码放入任何一种类型选择算法中。我们想要做的是让算法以某种方式存储和返回所选类型。这通常通过类型枚举,HashSet<decimal>对象等来完成。但我想要一个静态的强类型编译时解决方案。

2 个答案:

答案 0 :(得分:2)

由于C#不允许存储类型,我们需要存储“使用特定类型的能力”。

首先,我们需要一种表达类型相关操作的方法(例如,创建List<T>实例并将其传递给Console.WriteLine)。不幸的是.Net不允许为“非特定”泛型方法创建委托,但是我们可以通过接口获得类似的功能,因为它们支持泛型方法(参见Emulating delegates with free generic type parameters in C#)。

interface IGenericAction {
    void Do<T>();
}

class MyGenericListAction : IGenericAction {
    public void Do<T>() {
        Console.WriteLine(new List<T>());
    }
}

我们存储“使用特定类型执行某些类型相关操作的能力”:

public static Action<IGenericAction> Store<T>() {
    return action => action.Do<T>(); //Returns a function that, given a type-dependent action (IGenericAction), invokes it with type T
}

然后我们可以使用存储的类型:

public static void Use(Action<IGenericAction> genericInvoker) {
    genericInvoker(new MyGenericListAction()); //genericInvoker will call MyGenericListAction.Do<T>() using the stored type T
}

让我们测试一下:

var storedType = TypeHelpers.Store<int>();
Use(storedType);
//System.Collections.Generic.List`1[System.Int32] is printed on the console

答案 1 :(得分:0)

C#(或一般的.NET)是强类型的。这特别适用于泛型。如果创建泛型类型的实例,则type参数将在编译时固定。我不是百分百肯定它,但我认为,编译内部为具有特定类型参数的泛型生成一个全新的类型。这使得无法存储类型并在以后使用它来创建List的实例。无论如何,在调用Store时,你必须知道T,从那时起我们就会被强类型化。你可以用

public static Something<T> Store<T>()
{
    //whatever
}

然后致电

public static void Use<T>(Something<T> yourStoredSomething)
{
    yourStoredSomething.DoSomething();
}

无论如何,你仍然无法动态使用T.另一方面,如果我们从接口

派生出通用的东西
interface ISomething
{
    void DoSomething();
}

我们可以做到

public static ISomething Store<T>()
{
    return new Something<T>();
}

并且可以使用以下内容:

ISomething mySomething = null;
if(t == "string")
{
    mySomething = Storer.Store<string>();
}
else if(t == "int")
{
    mySomething = Storer.Store<int>();
}

请注意,这是您的问题的解决方案,但可能不是最好的解决方案。正如我之前所说的那样,泛型是非常类型的,这不是他们应该使用的方式。我以一种我厌恶的方式滥用泛型,我提供的解决方案主要是为了学术目的,而不是用于真正的程序。可能你应该改变你的方法来解决你遇到的问题,以实现更好的设计。