C#通用接口和工厂模式 [英] C# Generic Interface and Factory Pattern

查看:242
本文介绍了C#通用接口和工厂模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create a Generic interface where the parameter type of one of the methods is defined by the generic

EDIT

在意识到我可能已经通过在Factory创建方法中指定类型参数使事情感到困惑之后,我对问题进行了一些更改。我需要对第三方API进行两种类型的API调用。第一个使用int的ID从API检索记录。第二个也从API检索一条记录,但Id是一个字符串(guid)。对于每种记录类型(ClientEntity和InvoiceEntity),我都有一个类,它们都实现了我在其中传递ID类型的通用接口

I've changed the question slightly after realising I have probably confused matters by specifying a type parameter in the Factory creation method. What I have is two types of API calls that I need to make to a 3rd party API. The first retrieves a record from the API using an Id that is an int. The second also retrieves a record from the API but the Id is a string (guid). I have a class for each record type (ClientEntity and InvoiceEntity) that both implement a Generic Interface where I pass in the Id type

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

This is the Interface in which I declare a Method with an id Parameter

public interface IGeneric<TId>
{
    void ProcessEntity(TId id);
}

我在两个类中实现了接口,其中一个将id设置为

I implement the interface in a couple of classes, one sets the id to be an int, the other a string.

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
    }
}

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

What I would like to know is how do I use this within a factory pattern?

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;
    }

}

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

The objective is to use the factory to instantiate the correct class so that I can call the ProcessEntity method

EDIT

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

I don't want to have to pass in the Generic type to the factory method because the class that is created by the factory should handle that. When I create the object, I don't know what Id type is required, I want the factory to handle that

例如

   var myGeneric = GenericFactory.CreateGeneric("Client");
   myGeneric.ProcessEntity("guid")

   var myGeneric = GenericFactory.CreateGeneric("Invoice");
   myGeneric.ProcessEntity(1234)

我希望这是有道理的

推荐答案

您应该可以执行以下操作:

You should be able to do something like this:

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();
    }
}

您会这样使用:

var a = GenericFactory.CreateGeneric<string>();
var b = GenericFactory.CreateGeneric<int>();

请注意,这使用强类型调用而不是将类型名称作为字符串传递(

Note that this uses a strongly-typed call rather than passing in the type name as a string (which may or may not be what you actually want).

如果您想传递类型名称的字符串,您将必须返回对象,因为无法返回实际类型:

If instead you want to pass a string for the type name, you will have to return an object because there is no way to return the actual type:

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.");
    }
}

很明显,如果您有对象,通常必须将其转换为正确的类型才能使用(要求您知道实际的类型)。

Obviously if you have an object you would normally have to cast it to the right type in order to use it (which requires that you know the actual type).

另外,您可以使用反射来确定反射所包含的方法,然后以这种方式调用它们。但是,您仍然需要知道类型,才能传递正确类型的参数。

Alternatively, you could use reflection to determine what methods it contains, and call them that way. But then you'd still need to know the type in order to pass a parameter of the right type.

我认为您要在此处执行的操作不是正确的方法,一旦您开始尝试使用它,就会发现。

I think that what you are attempting to do here is not the right approach, which you will discover once you start trying to use it.

棘手的解决方案:使用 dynamic

Hacky solution: Use dynamic

不过,有一种方法可以使您获得想要的东西:使用 dynamic 如下所示(假设您从上面使用 object CreateGeneric(字符串类型)工厂方法):

Nevertheless, there is one way you can get something close to what you want: Use dynamic as follows (assuming that you are using the object CreateGeneric(string type) factory method from above):

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

a.ProcessEntity("A string");
b.ProcessEntity(12345);

请注意,动态使用反射和代码

Be aware that dynamic uses reflection and code generation behind the scenes, which can make the initial calls relatively slow.

还请注意,如果将错误的类型传递给通过访问的方法,则应注意动态,您会收到一个讨厌的运行时异常:

Also be aware that if you pass the wrong type to a method accessed via dynamic, you'll get a nasty runtime exception:

dynamic a = GenericFactory.CreateGeneric("string");
a.ProcessEntity(12345); // Wrong parameter type!

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

If you run that code, you get this kind of runtime exception:

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

这篇关于C#通用接口和工厂模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆