【问题标题】:Is there a way to specify the where T:new() restriction but with internal constructor?有没有办法指定 where T:new() 限制但使用内部构造函数?
【发布时间】:2012-05-19 22:50:01
【问题描述】:

我创建了一个需要实例化其实现类型的泛型类,因此实现类型必须具有可访问的无参数构造函数。看起来 new() 限制可以完成这项工作,但它强制实现类型在我拥有内部构造函数时具有公共构造函数(但可以访问,因为两者都在同一个程序集中)。

  1. 是否有理由强制它公开而不是“可访问”?
  2. 有什么方法可以满足我的需要吗?

提前致谢。

编辑:这样做的原因是我有一个必须通过单例使用的 X 类。 Singleton 类是一个泛型类,我想将类 X 构造函数设为内部以避免外部用户以错误的方式访问对象(调用构造函数)。

【问题讨论】:

  • 使类内部化,构造函数公开。如果课程必须是公开的,那么不,你不能。

标签: c# .net generics restriction


【解决方案1】:

是否有理由强制它公开而不是“可访问”?

术语可访问性非常上下文敏感,泛型不是,按架构。 在您的具体情况下,internal 可以访问,但 generics 是为通用解决方案而设计的。

有什么方法可以满足我的需要吗?

根据第一点,不,这是不可能的,我也不知道。

【讨论】:

  • 好吧,在我看来,编译器可以在编译时检查构造函数是否可以访问,或者只有当不能从泛型类调用 new 时才会引发错误。无论如何,谢谢你的解释。
  • @SoMoS:问题是具有new() 约束的类型T 很可能会传递给不同程序集中的例程。虽然在某些情况下编译器可能会确定特定类型参数 T 永远不能在特定程序集之外传递,但在许多情况下,这种推断是不可能的。仅当编译器可以推断出它是安全的时,才允许具有 internal 无参数构造函数的类型满足 new() 约束,这意味着对于何时允许这种用法有相当奇怪的规则。
  • @SoMos:您要求的是一些特殊的internal new() 定义,您可以在其中明确定义它。为什么现在不推断呢?我认为这是关于不同程序集之间的类型共享。如果我想使用无法从“外部”访问 ctor 的某个运行时类型的泛型,会发生什么......
  • @Tigran:当然,当运行时出现问题时总是会发生什么,引发异常。我真的不明白为什么在编译时强制执行此约束而不是在运行时执行。
  • @SoMoS: 可能是因为,就像 Eric Lippert 通常说的那样:“因为它没有实现”:)
【解决方案2】:

泛型用于通用解决方案,因此如果您使用“新”约束,您的解决方案必须与实现公共 ctor 的每个类一起使用。

如果您想为特定类型的类实现通用解决方案,您可以定义一个抽象基类来实现此类内部构造函数。为这个抽象基类实现一个通用的解决方案并使用

* 其中 T : MyBaseClassWithInternalCtor *

作为约束。

【讨论】:

  • 听起来不错的解决方案。谢谢!
【解决方案3】:

C# 语言不允许这样做,如规范绑定和未绑定类型第 4.4.3 节所述。

如果约束是构造函数约束new(),则类型A不能是abstract,并且必须有一个公共的无参数构造函数。如果满足以下条件之一,则满足此条件。

  • A 是一个值类型,因为所有值类型都有一个公共的默认构造函数
  • A 是具有协程约束的类型参数
  • A 是具有值类型约束的类型参数
  • A 是一个不是 abstract 的类,它包含一个显式声明的 public 构造函数,没有参数
  • A 不是 abstract 并且有一个默认构造函数。

如果不满足其中任何一个条件,则会出现编译器错误。如果您发现自己的类型是公共的,但只有内部构造函数,那么它们很可能实际上应该是具有公共构造函数的内部类型。

我建议将您的类型访问器更改为internal,并将其构造函数更改为public,并使其无参数。然后,您的 public 无参数构造函数可以调用非无参数 privateinternal 构造函数来执行任何额外的初始化工作。

internal class C<T> where : T new()
{
    public C() : this(new T()) {
    }

    private C(T t) {
        // Do additional initialization
    }
}

请注意,该模式是有限的,但没有什么能阻止您改用 private 方法。

internal class C<T> where T : new() {
    public C() {
        T t = new T();
        InitializeClass(t);
    }

    private void InitializeClass(T t) {
        throw new NotImplementedException();
    }
}   

根据您的更新,这是一个公共单身模式的小示例。

public class Singleton<T> where T : new()
{
    public static Singleton<T> Current {
        get;
        private set;
    }

    internal Singleton() : this(new T()) {
    }

    private Singleton(T t) {
        Current = this;
        // Do whatever you need to with T
    }        

    public String Name {
        get;
        set;
    }
}

用法

// Somewhere in your internal assembly
Singleton<String> singleton = new Singleton<String>();

// In an external assembly
Singleton.Current.Name = "SoMoS";

您甚至不需要以这种方式使用构造函数,您可以轻松地做一些简单的事情。

public class Singleton<T> where T : new()
{
    public static Singleton<T> Current {
        get;
        private set;
    }

    internal Singleton() {
        T t = new T();
        // Do stuff with T
    }

    public String Name {
        get;
        set;
    }
}

泛型如果你不能设计它来满足你的要求,它可能不是要走的路。泛型只能做这么多,并不能解决所有问题。有工厂模式、注入等。

【讨论】:

  • 谢谢,查看我的编辑以了解内部构造函数的原因
  • 更新了我的答案。您可能会多考虑一下您的设计,因为泛型可能不是最合适的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-10
  • 1970-01-01
  • 1970-01-01
  • 2014-04-12
  • 1970-01-01
  • 2018-04-21
  • 1970-01-01
相关资源
最近更新 更多