10

有办法吗?

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

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

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

该接口将在程序集内部

如果您对这种没有接口的场景有建议,我很乐意考虑...

4

10 回答 10

22

不要太直率,但是您误解了接口的目的。

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

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

于 2008-08-25T21:19:44.450 回答
6

您可以使用类型参数约束

interface ITest<T> where T: new()
{
    //...
}

class Test: ITest<Test>
{
    //...
}
于 2008-08-25T22:30:54.340 回答
5

胡安

不幸的是,没有办法用强类型语言解决这个问题。您将无法确保在编译时这些类将能够被您的基于 Activator 的代码实例化。

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

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

构造函数和静态方法必须具有在调用时可用的显式(代码中的)类型信息。这是必需的,因为没有涉及的类的实例可以由运行时查询以获取底层类型,运行时需要确定调用哪个实际的具体方法。

接口、抽象类或虚拟方法的全部意义在于能够在没有显式类型信息的情况下进行函数调用,这是通过引用一个实例来实现的,该实例具有“隐藏”类型信息而不是直接提供给调用代码。因此,这两种机制非常简单地相互排斥。它们不能一起使用,因为当你混合它们时,你最终在任何地方都没有具体的类型信息,这意味着运行时不知道在哪里可以找到你要求它调用的函数。

于 2008-08-25T21:54:47.080 回答
5

胡安·曼努埃尔说:

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

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

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

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

某些东西派生自 ITest<T>,但没有实现无参数构造函数。它会编译得很好,因为 String 确实实现了一个无参数的构造函数。同样,约束在 T 上,因此在 String 上,而不是在 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() { }
}
于 2008-08-26T05:35:16.880 回答
4

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

public interface IInterface
{
    void DoSomething();
}

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

使用 Type 非常难看,但在某些情况下是有意义的:

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 才是真正重要的。Factory 只是提供默认构造函数的类的便利类。这是最简单且通常最好的解决方案。

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

public IInterface CreateUsingDelegate(Func<IInterface> createCallback)
{
    return createCallback();
}

public void Test()
{
    IInterface thing = CreateUsingDelegate(() => new Foo());
}

这里的优点是代码简短,可以使用任何构造方法,并且(使用闭包)可以让您轻松传递构造对象所需的额外数据。

于 2008-09-16T00:54:28.360 回答
1

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

void RegisterType<T>() where T:ITest, new() {
}
于 2008-08-31T15:36:30.967 回答
0

我不这么认为。

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

于 2008-08-25T21:10:24.970 回答
0

我想提醒大家:

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

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

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

于 2008-08-25T22:15:37.530 回答
0

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

interface FooFactory {
    Foo createInstance();
}

对于 Foo 的每个实现,您都创建一个知道如何创建它的 FooFactory 实例。

于 2008-08-25T22:31:33.437 回答
0

Activator 不需要无参数构造函数来实例化您的类。您可以有一个参数化的构造函数并从 Activator 传递所有参数。查看MSDN 上的这个

于 2008-08-25T22:42:16.937 回答