【问题标题】:How to "store" a type (not `Type` object) for future use?如何“存储”一个类型(不是`Type`对象)以备将来使用?
【发布时间】:2014-01-07 22:31:37
【问题描述】:

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

假设有阶段。在第一阶段,您可以访问某些类型 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&lt;&gt;/LinkedList&lt;&gt;/HashSet&lt;&gt;/等)。每种类型都有许多可能的类型,并且整个可能的类型集是未知的。现在我们需要编写创建类型化容器的核心代码(例如HashSet&lt;decimal&gt;)。我们不能将这样的代码放入任何一种类型选择算法中。我们想要做的是要求算法以某种方式存储和返回选定的类型。这通常通过类型枚举、System.Type 对象等来完成。但我想要一个静态的强类型编译时解决方案。

【问题讨论】:

  • 你知道Type类,还有反射吗?
  • 您是否提前知道类型(T 是什么)或者您是否尝试动态实例化类型?
  • 你不能List&lt;Type&gt;是什么意思?这很好用(假设你的意思是Type 大写T)。
  • @Dialectus 是的,我知道System.Type(阅读问题)。
  • @SiLo 假设我们正在编写一个库。我们不知道用户稍后会给我们的具体T。当Store&lt;T&gt;()方法被执行时,显然有T。但我们想保存它以备将来使用。

标签: c# design-patterns types


【解决方案1】:

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

首先,我们需要一种方法来表达依赖于类型的操作(例如,创建一个List&lt;T&gt; 实例并将其传递给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

【讨论】:

    【解决方案2】:

    C#(或一般的 .NET)是强类型的。这尤其适用于泛型。如果创建泛型类型的实例,则类型参数在编译时固定。我对此不是百分百确定,但我认为,编译在内部为具有特定类型参数的泛型生成了一个全新的类型。这使得存储类型并在以后使用它来创建 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>();
    }
    

    请注意,这是您的问题的解决方案,但可能不是最好的解决方案。正如我之前所说,泛型是严格类型的,这不是它们应该使用的方式。我以一种我厌恶的方式滥用泛型,我主要出于学术目的提供解决方案,而不是在实际程序中使用。也许你应该改变你的方法来解决你遇到的问题,以实现更好的设计。

    【讨论】:

    • "Use&lt;T&gt;" - 这违背了存储的整个想法。如果我想使用它时可以使用它,我就不需要保存该类型。 “泛型是强类型的,这不是它们应该使用的方式” - 是的,我想要的是一个强类型的解决方案。我找到了这样的解决方案 - 通过 lambdas 和通用方法保存类型:stackoverflow.com/a/20983343/1497385
    猜你喜欢
    • 2022-06-16
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 2014-02-24
    • 1970-01-01
    • 2020-05-29
    • 1970-01-01
    • 2021-01-09
    相关资源
    最近更新 更多