代码之家  ›  专栏  ›  技术社区  ›  juan

对于实现接口的类型,如何需要不带参数的构造函数?

  •  10
  • juan  · 技术社区  · 16 年前

    有办法吗?

    我需要实现特定接口的所有类型都有一个无参数的构造函数,可以吗?

    我正在为公司中的其他开发人员开发用于特定项目的基本代码。

    有一个过程将创建执行特定任务的类型实例(在不同的线程中),我需要这些类型遵循特定的约定(ergo,接口)。

    接口将在程序集内部

    如果您对这个没有接口的场景有一个建议,我很乐意考虑它…

    10 回复  |  直到 16 年前
        1
  •  5
  •   Community Keith    7 年前

    Juan Manuel said:

    这就是我不明白为什么它不能成为接口中合同的一部分的原因之一。

    这是一种间接机制。通用允许您“欺骗”并随接口一起发送类型信息。这里要记住的关键是约束不在您直接使用的接口上。它不是对接口本身的约束,而是对接口上的其他类型的约束。恐怕这是我能提供的最好的解释。

    为了说明这个事实,我将指出一个我在AKU代码中注意到的漏洞。可以编写一个编译良好但在运行时尝试实例化时失败的类:

    public class Something : ITest<String>
    {
      private Something() { }
    }
    

    某些内容派生自ITest<t>,但不实现无参数构造函数。它编译得很好,因为字符串确实实现了无参数的构造函数。同样,约束是在t上的,因此是字符串,而不是ITest或其他东西。因为满足了对t的约束,所以将编译它。但它会在运行时失败。

    预防 一些 对于这个问题的实例,您需要向t添加另一个约束,如下所示:

    public interface ITest<T>
      where T : ITest<T>, new()
    {
    }
    

    注意新的限制:t:itest<t>。此约束指定传递到ITest<t>参数的内容 必须 得到 来自ITest<t>。

    即使这样,这也不能阻止 全部的 洞的箱子。下面的代码编译得很好,因为a有一个无参数的构造函数。但由于B的无参数构造函数是私有的,所以用进程实例化B在运行时将失败。

    public class A : ITest<A>
    {
    }
    
    public class B : ITest<A>
    {
      private B() { }
    }
    
        2
  •  21
  •   Brad Wilson    16 年前

    不要太直率,但您误解了接口的用途。

    接口意味着有几个人可以在自己的类中实现它,然后将这些类的实例传递给其他要使用的类。创建会产生不必要的强耦合。

    听起来您真的需要某种注册系统,要么让人们注册实现接口的可用类的实例,要么让工厂根据请求创建这些项。

        3
  •  5
  •   Chris Ammerman    16 年前

    胡安

    不幸的是,无法用强类型语言解决这个问题。您将无法确保在编译时类能够由基于激活器的代码实例化。

    (编辑:删除了错误的替代解决方案)

    原因是,不幸的是,不可能将接口、抽象类或虚拟方法与构造函数或静态方法结合使用。简单的原因是前者不包含显式类型信息,后者需要显式类型信息。

    构造器和静态方法 必须 在调用时具有明确的(代码中的)类型信息。这是必需的,因为没有相关类的实例,运行时可以查询这些实例以获取底层类型,运行时需要确定要调用的实际具体方法。

    接口、抽象类或虚拟方法的整个点是能够进行函数调用 没有 显式类型信息,这是由于引用了一个实例,而该实例的“隐藏”类型信息对调用代码不直接可用。所以这两种机制是完全互斥的。它们不能一起使用,因为当您混合它们时,在任何地方都没有具体的类型信息,这意味着运行时不知道在哪里可以找到您要调用的函数。

        4
  •  5
  •   aku    16 年前

    你可以使用 type parameter constraint

    interface ITest<T> where T: new()
    {
        //...
    }
    
    class Test: ITest<Test>
    {
        //...
    }
    
        5
  •  4
  •   munificent    16 年前

    所以你需要一个 事情 它可以创建实现接口的未知类型的实例。基本上有三个选项:工厂对象、类型对象或委托。这是Givens:

    public interface IInterface
    {
        void DoSomething();
    }
    
    public class Foo : IInterface
    {
        public void DoSomething() { /* whatever */ }
    }
    

    使用类型很难看,但在某些情况下是有意义的:

    public IInterface CreateUsingType(Type thingThatCreates)
    {
        ConstructorInfo constructor = thingThatCreates.GetConstructor(Type.EmptyTypes);
        return (IInterface)constructor.Invoke(new object[0]);
    }
    
    public void Test()
    {
        IInterface thing = CreateUsingType(typeof(Foo));
    }
    

    最大的问题是,在编译时,您不能保证foo 默认构造函数。另外,如果这恰好是性能关键的代码,则反射速度会慢一些。

    最常见的解决方案是使用工厂:

    public interface IFactory
    {
        IInterface Create();
    }
    
    public class Factory<T> where T : IInterface, new()
    {
        public IInterface Create() { return new T(); }
    }
    
    public IInterface CreateUsingFactory(IFactory factory)
    {
        return factory.Create();
    }
    
    public void Test()
    {
        IInterface thing = CreateUsingFactory(new Factory<Foo>());
    }
    

    在上面,iFactory才是真正重要的。工厂只是一个方便班 提供默认构造函数。这是最简单且通常是最好的解决方案。

    第三个目前不常见但可能变得更常见的解决方案是使用委托:

    public IInterface CreateUsingDelegate(Func<IInterface> createCallback)
    {
        return createCallback();
    }
    
    public void Test()
    {
        IInterface thing = CreateUsingDelegate(() => new Foo());
    }
    

    这里的优点是代码简短,可以与 任何 构造方法和(使用闭包)使您可以轻松地传递构造对象所需的其他数据。

        6
  •  1
  •   Mark Brackett Achilles Ram Nakirekanti    16 年前

    使用该类型调用RegisterType方法,并使用泛型对其进行约束。然后,不必遍历程序集来查找ITest实现者,只需存储它们并从中创建。

    void RegisterType<T>() where T:ITest, new() {
    }
    
        7
  •  0
  •   Vaibhav    16 年前

    我不这么认为。

    您也不能为此使用抽象类。

        8
  •  0
  •   Frank Krueger    16 年前

    我想提醒大家:

    1. 在.NET中编写属性很容易
    2. 在.NET中编写静态分析工具,以确保符合公司标准很容易

    编写一个工具来获取实现某个接口/具有一个属性的所有具体类,并验证它是否具有无参数构造函数需要大约5分钟的编码工作。您将它添加到构建后的步骤中,现在您就有了一个框架,可以执行任何其他需要执行的静态分析。

    语言,编译器,IDE,你的大脑——它们都是工具。使用它们!

        9
  •  0
  •   Landon Kuhn    16 年前

    不,你不能那样做。也许对于您的情况,工厂界面会有帮助?类似:

    interface FooFactory {
        Foo createInstance();
    }
    

    对于foo的每个实现,都要创建一个知道如何创建foofactory的实例。

        10
  •  0
  •   Andrei Rînea    16 年前

    您不需要一个无参数的构造函数来实例化您的类。您可以有一个参数化的构造函数并从激活器传递所有参数。退房 MSDN on this .