代码之家  ›  专栏  ›  技术社区  ›  Ian Mercer

用于静态泛型类?

  •  23
  • Ian Mercer  · 技术社区  · 16 年前

    A的主要用途是什么 静态的 C?中的泛型类应该什么时候使用?哪些例子最能说明它们的用法?

    例如

    public static class Example<T>
    {
       public static ...
    }
    

    由于无法在其中定义扩展方法,因此它们的实用性似乎有些有限。关于这个话题的网络参考文献很少,所以很明显没有很多人在使用它们。这里有几个:

    http://ayende.com/Blog/archive/2005/10/05/StaticGenericClass.aspx

    Static Generic Class as Dictionary


    回答摘要

    关键问题似乎是 具有静态方法的静态泛型类 和A 具有静态泛型成员的非泛型静态类 “

    关于使用哪种类型的决定似乎是围绕“类是否需要在内部存储类型特定的状态?”

    如果不需要特定于类型的内部存储,那么具有泛型静态方法的静态非泛型类似乎更可取,因为调用语法更好,并且可以在其中定义扩展方法。

    9 回复  |  直到 9 年前
        1
  •  21
  •   Greg    16 年前

    我使用静态泛型类来缓存反射繁重的代码。

    假设我需要构建一个实例化对象的表达式树。我在类的静态构造函数中构建一次,将其编译为lambda表达式,然后将其缓存到静态类的一个成员中。我通常不让这些课程公开评估——它们通常是其他课程的助手。通过以这种方式缓存我的表达式,我可以避免以某种方式缓存我的表达式 Dictionary<Type, delegate> .

    在 BCL . ( DataRow )扩展方法 Field<T>() 和 SetField<T>() 使用(私有)静态泛型类 System.Data.DataRowExtensions+UnboxT<T> . 用反光镜检查一下。

        2
  •  11
  •   Community Mohan Dere    9 年前

    上课 static 不添加任何功能-这只是一个方便的检查,如果你想使用一个类而不实例化它。它有很多用途…

    您可以使用静态泛型类来绕过一个限制:c不允许部分专门化。这意味着您必须指定所有类型参数或不指定任何类型参数。然而,这可能是不必要的冗长。

    例如:

    static class Converter {
        public TOut Convert<TIn, TOut>(TIn x) {...}
    }
    

    前一个类不允许类型推断,因为推断对返回值不起作用。但是,您也不能在不指定输入类型的情况下指定返回值类型,因为您不能部分专门化。使用(可能是静态的)泛型类,只能指定以下两种类型之一:

    static class ConvertTo<TOut> {
        public TOut Convert<TIn>(TIn x) {...}
    }
    

    这样就可以让类型推断在参数类型上工作,并且只指定返回类型。

    (尽管上述情况是可以想象的,但它当然不要求泛型类是静态的)。


    第二,(作为史蒂文 first pointed out )每个构造类型都有一个单独的静态字段,这使得静态类成为存储有关类型或类型组合的额外信息的好地方。本质上,它是一个半静态哈希表,可以对类型进行键控。

    半静态查找表键入类型听起来有点奇怪,但它实际上是一个非常非常有用的结构,因为它允许您将昂贵的反射和代码生成结果存储在几乎可以自由查找的地方(比字典便宜,因为它可以进行jit,而且您可以避免对…的呼唤 .GetType() )如果你在做元编程,这是伟大的!

    例如,我在 ValueUtils 要存储生成的哈希函数:

    //Hash any object:
    FieldwiseHasher.Hash(myCustomStructOrClass);
    
    //implementation:
    public static class FieldwiseHasher {
        public static int Hash<T>(T val) { return FieldwiseHasher<T>.Instance(val); }
    }
    
    public static class FieldwiseHasher<T> {
        public static readonly Func<T, int> Instance = CreateLambda().Compile();
        //...
    }
    

    静态泛型方法允许 类型推断 使使用变得非常容易;泛型类上的静态字段实际上允许无开销 (元数据)存储 . 如果orm是 Dapper 和 PetaPoco 使用像这样的技术;但它也非常适合(反)序列化程序。一个限制是,由于绑定到 编译时间 类型;如果传递的对象实际上是子类的实例,则可能绑定到了错误的类型,并且添加检查以避免这种情况损害了低开销的好处。

        3
  •  10
  •   Steven    9 年前

    泛型类型的静态字段特定于实际类型T。这意味着您可以在内部存储特定于类型的缓存。这可能是创建静态泛型类型的原因。下面是一个(相当无用,但信息丰富)的例子:

    public static TypeFactory<T> where T : new()
    {
        // There is one slot per T.
        private static readonly object instance = new T();
    
        public static object GetInstance() {
            return instance;
        }
    }
    
    string a = (string)TypeFactory<string>.GetInstance();
    int b = (int)TypeFactory<int>.GetInstance();
    
        4
  •  5
  •   Chris Taylor    16 年前

    我最近学到的静态泛型类的一个用途是在类级别上为类型定义一个约束,然后该约束应用于类的所有静态成员,我还学到的是,对于定义扩展方法的静态泛型类,这是不允许的。ODS。

    例如,如果您要创建一个静态泛型类,并且您知道所有泛型类型都应该是IComparable(只是一个示例),那么您可以执行以下操作。

    static class MyClass<T> where T : IComparable
    {
      public static void DoSomething(T a, T b)
      {
      }
    
      public static void DoSomethingElse(T a, T b)
      {
      }
    }
    

    注意,我不需要对所有成员应用约束,而只需要在类级别应用约束。

        5
  •  3
  •   Justin Niessner    16 年前

    您提到的第一篇博客文章显示了一个有效的用法(作为activerecord实现的静态存储库类):

    public static class Repository<T>
    {
        public static T[] FindAll { }
    
        public static T GetById(int id){ }
    
        public static void Save(T item) { }
    }
    

    或者如果要为泛型数组实现不同的排序方法:

    public static class ArraySort<T>
    {
        public static T[] BubbleSort(T[] source) { }
    
        public static T[] QuickSort(T[] source) { }
    }
    
        6
  •  2
  •   Juliet    16 年前

    静态的主要用途是什么 C?中的泛型类他们应该什么时候 使用吗?什么样的例子最能说明问题 它们的用法?

    我认为一般来说,应该避免在静态类上创建类型参数,否则就不能依赖类型推断来使客户端代码更简洁。

    举一个具体的例子,假设您正在编写一个静态实用程序类来处理列表上的操作。你可以用两种方式写这门课:

    // static class with generics
    public static class ListOps<T>
    {
        public static List<T> Filter(List<T> list, Func<T, bool> predicate) { ... }
        public static List<U> Map<U>(List<T> list, Func<T, U> convertor) { ... }
        public static U Fold<U>(List<T> list, U seed, Func<T, U> aggregator) { ... }
    }
    
    // vanilla static class
    public static class ListOps
    {
        public static List<T> Filter<T>(List<T> list, Func<T, bool> predicate) { ... }
        public static List<U> Map<T, U>(List<T> list, Func<T, U> convertor) { ... }
        public static U Fold<T, U>(List<T> list, U seed, Func<T, U> aggregator) { ... }
    }
    

    但是类是等价的,但是哪个更容易使用呢?比较:

    // generic static class
    List<int> numbers = Enumerable.Range(0, 100).ToList();
    List<int> evens = ListOps<int>.Filter(numbers, x => x % 2 = 0);
    List<string> numberString = ListOps<int>.Map(numbers, x => x.ToString());
    int sumOfSquares = ListOps<int>.Fold(numbers, 0, (acc, x) => acc + x*x);
    
    // vanilla static class
    List<int> numbers = Enumerable.Range(0, 100).ToList();
    List<int> evens = ListOps.Filter(numbers, x => x % 2 = 0);
    List<string> numberString = ListOps.Map(numbers, x => x.ToString());
    int sumOfSquares = ListOps.Fold(numbers, 0, (acc, x) => acc + b * b);
    

    在我看来, ListOps<someType> 又笨重又笨拙。Vanilla类的定义稍大一点,但由于类型推断,客户端代码更容易阅读。

    在最坏的情况下,c无法推断类型,您必须手动指定它们。你愿意写信吗 ListOps<A>.Map<B>(...) 或 ListOps.Map<A, B>(...) ?我更喜欢后者。


    当静态类保持时,上面的策略特别有效 无可变状态 ,或其可变状态已知 编译时 .

    如果静态类持有不在编译时确定其类型的可变状态,那么您可能有一个带有泛型参数的静态类的用例。希望这些场合很少,但是当它发生的时候,你会很高兴C支持这个功能。

        7
  •  2
  •   Lee Salter    9 年前

    在测试使用EntityFramework异步方法的类时,我使用这些来模拟dbset。

    public static class DatabaseMockSetProvider<TEntity> where TEntity: class
    {
        public static DbSet<TEntity> CreateMockedDbSet(IQueryable<TEntity> mockData)
        {
            var mockSet = Mock.Create<DbSet<TEntity>>();
            Mock.Arrange(() => ((IDbAsyncEnumerable<TEntity>)mockSet).GetAsyncEnumerator())
                .Returns(new TestDbAsyncEnumerator<TEntity>(mockData.GetEnumerator()));
            Mock.Arrange(() => ((IQueryable<TEntity>)mockSet).Provider)
                .Returns(new TestDbAsyncQueryProvider<TEntity>(mockData.Provider));
            Mock.Arrange(() => ((IQueryable<TEntity>)mockSet).Expression).Returns(mockData.Expression);
            Mock.Arrange(() => ((IQueryable<TEntity>)mockSet).ElementType).Returns(mockData.ElementType);
            Mock.Arrange(() => ((IQueryable<TEntity>)mockSet).GetEnumerator()).Returns(mockData.GetEnumerator());
    
            return mockSet;
        }
    }
    

    像在我的单元测试中那样使用它们-节省了很多时间,可以用于任何实体类型:

    var mockSet = DatabaseMockSetProvider<RecipeEntity>.CreateMockedDbSet(recipes);
            Mock.Arrange(() => context.RecipeEntities).ReturnsCollection(mockSet);
    
        8
  •  1
  •   Steven Sudit    16 年前

    你说得对:它们没有多大用处。不过,也许有一些罕见的例外情况。例如,如果类是特定于类型的存储库(如justin的示例中所示),但将静态集合保留为缓存,该怎么办?一般来说,如果它包含状态,而不仅仅是方法,那么这可能是有意义的。

        9
  •  0
  •   John Saunders    16 年前

    静态泛型类与任何给定的静态类一样有用。不同的是,您不必使用copy和paste为每种类型创建静态类的版本。使类成为泛型,并且可以为每个类型参数集“生成”一个版本。