繁体   English   中英

c# generics class 工厂问题

[英]c# generics class factory question

我想控制一堆类的创建,这些类都共享一个公共接口,并且在构造中都需要一些逻辑。 此外,除了 class 工厂之外,我不希望任何其他代码能够从这些类创建对象。

我的主要绊脚石是:

(1) 为了使泛型方法能够创建类的实例,我需要 new() 约束,这意味着我必须在类上有一个公共构造函数,这意味着它们可以公开创建。

(2) 另一种方法是类本身具有 static 方法,该方法返回 class 的实例。 但是我不能从我的通用 class 中调用它,因为我需要处理接口/类型,并且您不能通过接口获得静态。

这是我目前拥有的那种东西,但它使用了 new() 约束,它允许我的类被公开创建:

internal static class MyClassFactory
{
    internal static T Create<T>(string args) where T : IMyType, new()
    {
        IMyType newThing = new T();
        newThing.Initialise(string args);
        return (T)newThing;
    }
}

public interface IMyType
{
    void Initialise(string args);
}

public class ThingA: IMyType
{
public void Initialise(string args)
{
        // do something with args
}
}

非常感谢任何帮助:)

看起来您正在尝试推出自己的服务定位器。

您是否考虑过采用依赖注入 (DI) 方法? 因为您可能希望避免使用服务定位器是有原因的。

我强烈建议您查看一些流行的 IOC 容器,它们可以执行您尝试构建的功能。 回想起来,我很高兴我选择了 DI 而不是服务定位器。

-忍者

- Autofac

-团结

你可以做一些事情,但它真的很难看......

public class ThingA: IMyType
{
    [Obsolete("This constructor must not be called directly", true)]
    public ThingA()
    {
    }

    public void Initialise(string args)
    {
            // do something with args
    }

}

如果您尝试显式调用构造函数,这将导致编译错误,但不会阻止在具有new()约束的泛型方法中调用它。

您可以做的是制定一个(隐藏的)约定,即所有实现您的接口的对象也具有工厂可以调用的特定构造函数。 这就是 ISerializable 的工作原理。 缺点是编译器不检查构造函数的存在。 在您的工厂中,通过反射找到构造函数并使用正确的 arguments 调用。 构造函数可以被保护。

// Get constr with string arg
var constr = theType.GetConstructor(new[]{typeof(String)});

T result = (T)constr.Invoke(new[]{"argString"});

这可能对你有用——使用反射而不是 new() 约束。 另请注意,构造函数是私有的,您需要向派生的 class 添加一个方法,该方法返回自身的实例(静态):

internal static class MyClassFactory
{
    internal static T Create<T>(string args) where T : IMyType
    {
        IMyType newThing = 
           (T)typeof(T).GetMethod("GetInstance").Invoke(default(object), null); 

        newThing.Initialise(args);

        return (T)newThing; 
    }
}

public interface IMyType 
{
    void Initialise(string args); 
}  

public class ThingA: IMyType 
{
    private ThingA() { }

    public static IMyType GetInstance()
    {
        return new ThingA();  // control creation logic here
    }

    public void Initialise(string args) 
    {   
        // do something with args 
    } 
} 

编辑

只是为了完善这一点,正如所指出的,您可以通过反射访问私有构造函数,如下所示:

internal class MyClassFactory 
{
    internal static T Create<T>(string args) where T : IMyType
    {
        IMyType newThing = (T)typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, Type.EmptyTypes, null).Invoke(null); 

        newThing.Initialise(args);

        return (T)newThing; 
    }
}

public interface IMyType 
{
    void Initialise(string args); 
}

public class ThingA : IMyType
{
    private ThingA() { }

    public void Initialise(string args)
    {
        Console.WriteLine(args);
    }
} 

考虑一种反思方法。

只需将类的构造函数标记为私有,这样它们就不能以常规方式实例化,并且您的工厂将使用反射调用此私有 class 构造函数。

反射对性能有影响,但调用这个构造函数并不是一个大的反射操作。

查看此 MSDN 文章以了解有关如何使用反射调用私有构造函数的更多信息:

但可以用这个代码片段来概括:

typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.Private, Type.EmptyTypes, Type.DefaultBinder, null).Invoke(null);

更新

顺便说一句,我相信以任何方式影响性能和增加代码复杂性,因为您不希望开发人员仅通过使用这种工厂方法来实例化 class 并不是一个强有力的理由。

有时,一个好的开发者准则比任何代码约束都要好。 我这么说是因为,在你的情况下,我会使用 T 泛型参数和相同的泛型约束来实现该工厂方法,如果我的文档文件说“如果你想要一个 T 类型的实例,你需要使用这个工厂”有些人决定不遵守这条规则,这不是我的责任,任何麻烦都会用“手册说你需要使用工厂”来回答。

良好的习惯比处理人类决策的额外防御性代码更好。

使用“Create”方法定义接口 IFactory<out T>,然后为每个要创建的 class TT 定义一个实现 IFactory<TT> 的 static object。 然后,您可以在要创建 TT 的任何地方传递 IFactory<TT> object,或者您可以创建 static class 然后向您注册适当的 FactoryHolder<T>。 请注意,泛型类对于泛型类型的每种组合都有一组单独的 static 变量,这可以提供一种非常方便且类型安全的方法来存储类型和工厂实例之间的 static 映射。

一个小小的警告是,必须为想要找到的每一种确切类型注册一个工厂。 可以将派生类型工厂注册为基类型的生产者(例如 FactoryHolder<Car>.GetFactory() 将返回 IFactory<FordFocus2Door>),但除非明确注册给定确切类型的工厂T, FactoryHolder<T> 将出现空。 例如,IFactory<FordFocus2Door> 或 IFactory<FordFocus4Door> 都可以用作 IFactory<FordFocus>,但除非其中一个注册为 IFactory<FordFocus>,否则 FactoryHolder<FordFocus>.GetFactory 都不会返回()。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM