C#通用接口和工厂模式

Nic*_*ith 13 c# generics factory-pattern

我正在尝试创建一个Generic接口,其中一个方法的参数类型由泛型定义

编辑

我意识到我可能通过在Factory创建方法中指定一个类型参数来混淆问题后稍微改变了这个问题.我有两种类型的API调用,我需要对第三方API.第一个使用ID作为int从API检索记录.第二个也从API检索记录,但Id是一个字符串(guid).我有一个类用于每个记录类型(ClientEntity和InvoiceEntity),它们都实现了一个通用接口,我在其中传递Id类型

这是我在其中声明一个带有id参数的方法的接口

public interface IGeneric<TId>
{
    void ProcessEntity(TId id);
}
Run Code Online (Sandbox Code Playgroud)

我在几个类中实现了接口,一个将id设置为int,另一个设置为字符串.

public class ClientEntity: IGeneric<int> // Record with Id that is an int
{
    public void ProcessEntity(int id)
    {
        Console.WriteLine(id);
        // call 3rd party API with int Id
    }
}

public class InvoiceEntity: IGeneric<string> // Record with Id that is a string (guid)
{
    public void ProcessEntity(string id)
    {
        Console.WriteLine(id);
        // call 3rd party API with string Id
    }
}
Run Code Online (Sandbox Code Playgroud)

我想知道的是如何在工厂模式中使用它?

public static class GenericFactory
{
    public static IGeneric<WhatGoesHere> CreateGeneric(string recordType)
    {
        if (recordType == "Client")
        {
            return new ClientEntity();
        }
        if (type == "Invoice")
        {
            return new InvoiceEntity();
        }

        return null;
    }

}
Run Code Online (Sandbox Code Playgroud)

目标是使用工厂实例化正确的类,以便我可以调用ProcessEntity方法

编辑

我不想将Generic类型传递给factory方法,因为工厂创建的类应该处理它.当我创建对象时,我不知道需要什么Id类型,我希望工厂处理它

例如

   var myGeneric = GenericFactory.CreateGeneric("Client");
   myGeneric.ProcessEntity("guid")
Run Code Online (Sandbox Code Playgroud)

要么

   var myGeneric = GenericFactory.CreateGeneric("Invoice");
   myGeneric.ProcessEntity(1234)
Run Code Online (Sandbox Code Playgroud)

我希望这是有道理的

tym*_*167 14

通常对于那个工厂使用一些DI容器(例如,当GenericInt或GenericString具有依赖关系时DI可能很有用),但是为了演示如何解决这个问题:

void Main()
{
    GenericFactory.CreateGeneric<int>();
    GenericFactory.CreateGeneric<string>();
}

public static class GenericFactory
{
    private static Dictionary<Type, Type> registeredTypes = new Dictionary<System.Type, System.Type>();

    static GenericFactory()
    {
        registeredTypes.Add(typeof(int), typeof(GenericInt));
        registeredTypes.Add(typeof(string), typeof(GenericString));
    }

    public static IGeneric<T> CreateGeneric<T>()
    {
        var t = typeof(T);
        if (registeredTypes.ContainsKey(t) == false) throw new NotSupportedException();

        var typeToCreate = registeredTypes[t];
        return Activator.CreateInstance(typeToCreate, true) as IGeneric<T>;
    }

}

public interface IGeneric<TId>
{
    TId Id { get; set; }

    void ProcessEntity(TId id);
}

public class GenericInt : IGeneric<int>
{
    public int Id { get; set; }

    public void ProcessEntity(int id)
    {
        Console.WriteLine(id);
    }
}

public class GenericString : IGeneric<string>
{
    public string Id { get; set; }

    public void ProcessEntity(string id)
    {
        Console.WriteLine(id);
    }
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*son 13

你应该可以做这样的事情:

public static class GenericFactory
{
    public static IGeneric<T> CreateGeneric<T>()
    {
        if (typeof(T) == typeof(string))
        {
            return (IGeneric<T>) new GenericString();
        }

        if (typeof(T) == typeof(int))
        {
            return (IGeneric<T>) new GenericInt();
        }

        throw new InvalidOperationException();
    }
}
Run Code Online (Sandbox Code Playgroud)

你会像这样使用它:

var a = GenericFactory.CreateGeneric<string>();
var b = GenericFactory.CreateGeneric<int>();
Run Code Online (Sandbox Code Playgroud)

请注意,这使用强类型调用,而不是将类型名称作为字符串传递(可能是您实际想要的,也可能不是).


如果您想要为类型名称传递字符串,则必须返回一个,object因为无法返回实际类型:

public static object CreateGeneric(string type)
{
    switch (type)
    {
        case "string": return new GenericString();
        case "int":    return new GenericInt();
        default:       throw new InvalidOperationException("Invalid type specified.");
    }
}
Run Code Online (Sandbox Code Playgroud)

显然,如果你有一个,object你通常必须将它强制转换为正确的类型才能使用它(这要求你知道实际的类型).

或者,您可以使用反射来确定它包含的方法,并以这种方式调用它们.但是你仍然需要知道类型才能传递正确类型的参数.

我认为你在这里尝试做的不是正确的方法,一旦你开始尝试使用它,你会发现它.

Hacky解决方案:使用 dynamic

然而,有一种方法可以得到一些接近你想要的东西:使用dynamic如下(假设你使用object CreateGeneric(string type)上面的工厂方法):

dynamic a = GenericFactory.CreateGeneric("string");
dynamic b = GenericFactory.CreateGeneric("int");

a.ProcessEntity("A string");
b.ProcessEntity(12345);
Run Code Online (Sandbox Code Playgroud)

请注意,dynamic在幕后使用反射和代码生成,这可能会使初始调用相对较慢.

另请注意,如果将错误的类型传递给通过访问的方法dynamic,您将得到一个讨厌的运行时异常:

dynamic a = GenericFactory.CreateGeneric("string");
a.ProcessEntity(12345); // Wrong parameter type!
Run Code Online (Sandbox Code Playgroud)

如果运行该代码,则会出现此类运行时异常:

Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'ConsoleApplication1.GenericString.ProcessEntity(string)' has some invalid arguments
   at CallSite.Target(Closure , CallSite , Object , Int32 )
   at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite site, T0 arg0, T1 arg1)
   at ConsoleApplication1.Program.Main() in D:\Test\CS6\ConsoleApplication1\Program.cs:line 71
Run Code Online (Sandbox Code Playgroud)