在C#中实现泛型

时间:2016-09-16 03:55:02

标签: c# generics collections

我是学生,想在我的测试项目中实现泛型。以下是我的代码

class clsCallService
{
    public void CallServiceMethod(string serviceName)
    {
        if (serviceName == "WEB")
            Web_Service.AddData("4G", DateTime.Now, DateTime.Now, "test");
        else if (serviceName == "VOICE")
            Voice_Service.AddData("4G", DateTime.Now, DateTime.Now, "test");
        else if (serviceName == "VIDEO")
            Video_Service.AddData("4G", DateTime.Now, DateTime.Now, "test");
    }
}       

class Web_Service
{
    public static void AddData(string technology, DateTime SDate, DateTime EDate, String someVariable)
    { 
        //Call DAL method
    }
}

class Voice_Service
{
    public static void AddData(string technology, DateTime SDate, DateTime EDate, String someVariable)
    {
        //Call DAL method
    }
}

class Video_Service
{
    public static void AddData(string technology, DateTime SDate, DateTime EDate, String someVariable)
    {
        //Call DAL method
    }
}

现在,我想创建一个泛型类。在这个类中,应该有一个具有泛型参数的泛型方法。所以,我不需要像上面那样编写IF Condition。我只需要传递任何类和基类的对象,将调用方法。我正在尝试编写一些代码,但却无法做到。

public class testGenericClass<T> where T : Web_Service, new()
{
    public void CallServiceMethod(string technology, DateTime SDate, DateTime EDate, String someVariable)
    {
        T.AddData(technology, SDate, EDate, someVariable);
    }
}

请建议。

获得一些有用的回复后更新 -

更新了代码

public class createGenericClass<T> where T : IWebService,IVoiceService
{
    public void CallDoSomeThing(T t, int x, int y)
    {
        t.DoSomeThing(x, y);
    }

    public void CallDoSomeThingElse(T t, int a, float b)
    {
        t.DoSomeThingElse(a, b);
    }

}

public interface IWebService
{
    void DoSomeThing(int x, int y);        
}

public interface IVoiceService
{
    void DoSomeThingElse(int a, float b);
}

public class Web_Service : IWebService
{
    public void DoSomeThing(int x, int y)
    { }    
}    

public class Voice_Service : IVoiceService
{
    public void DoSomeThingElse(int a, float b)
    { }
}

现在,我有两种不同的界面,即网络和语音。两者都有不同的功能。我已经成功实现了它,但我无法调用我的方法。我不知道怎么打电话。下面的代码(只是尝试创建一个新实例。)

class Program
{
    static void Main(string[] args)
    {
        createGenericClass<IWebService> obj = new createGenericClass<IWebService>();

    }
}

请建议。

3 个答案:

答案 0 :(得分:2)

您无法在泛型类型上调用静态函数,因此我假设您正在尝试调用实例方法。在调用T

之前,您需要实例化.AddData(...)
public class testGenericClass<T> where T : Web_Service, new()
{
    public void CallServiceMethod(string technology, DateTime SDate, DateTime EDate, String someVariable)
    {
        new T().AddData(technology, SDate, EDate, someVariable);
    }
}

class Web_Service
{
    public void AddData(string technology, DateTime SDate, DateTime EDate, String someVariable)
    { 
        //Call DAL method
    }
}

答案 1 :(得分:2)

声明所有类都将实现的接口,例如

interface IService 
{
    void AddData(string technology, DateTime SDate, DateTime EDate, String someVariable); 
}

然后每个类实现,如:

public class Web_Service : IService
{
    public void AddData(string technology, DateTime SDate, DateTime EDate, String someVariable) {
        // do stuff... 
    }
}
// etc...

我认为值得指出的是,如果你使用这样的界面,你可以接受IService类型的参数,根本不需要泛型。

public void CallServiceMethod(IService service, string technology, DateTime SDate, DateTime EDate, String someVariable) 
{
    service.AddData(technology, SDate, EDate, someVariable); 
}

如果由于某种原因需要使用泛型方法的东西,那么可以编写一个泛型方法,由接口约束,

public class testGenericClass<T> where T : IService
{
    public void CallServiceMethod(T t, string technology, DateTime SDate, DateTime EDate, String someVariable)
    {
        t.AddData(technology, SDate, EDate, someVariable);
    }
}

请注意,此方法要求接口方法AddData()是实例方法,而不是像现在这样的静态方法。

答案 2 :(得分:0)

当您查看.NET基类库中如何使用泛型时,您将看到它主要用于枚举和集合(例如List<T>IEnumerable<T>),即数据结构无论基础类型如何,行为都相同。经验法则通常是:如果你需要检查泛型方法中的确切typeof(T),你可能做错了。

但是,在泛型方法中检查typeof(T)的一个原因是工厂方法,即"service locators",它允许您获取某种类型的具体实例。这意味着你将有一个特殊的类在程序启动时注册各种接口的具体实现,这样你的程序就可以获得具体的依赖,而不知道它们是如何被实例化的。

虽然实际上通过constructor dependency injection实现了纯粹的控制反转,但服务定位器方法提供了更好的设计,只需手动设置依赖关系(使用new关键字)。但是,单元测试稍微困难一些,因为您的dependencies still remain opaque to callers

考虑类似的事情:

public interface IServiceLocator
{
    T GetService<T>();
}

// you use it whenever you would use `new` otherwise,
// to get a concrete instance of some interface, and
// you don't know and don't care where this instance 
// came from:

var webService = ServiceLocator.GetService<IWebService>();
var videoService = ServiceLocator.GetService<IVideoService>();

这个服务定位器可以是一个框架,或者你可以自己编写,简单如下:

public class ServiceLocator : IServiceLocator
{
    readonly Dictionary<Type, Func<object>> _typeFactories =
        new Dictionary<Type, Func<object>>();

    public ServiceLocator()
    {
        // register a factory method for each service
        _typeFactories[typeof(IWebService)] = () => new SomeWebService();
        _typeFactories[typeof(IVideoService)] = () => new SomeVideoService();

        // services can also be singletons, if they can be shared
        _typeFactories[typeof(IVoiceService)] = () => VoiceService.Instance;
    }

    public T GetService<T>()
    {
        // get the factory method and invoke it
        // (you will probably want to have some checks here)

        var factory = _typeFactories[typeof(T)];
        return factory();
    }
}

但是,在您的情况下,似乎所有三个服务共享一个公共接口,因此不需要泛型。您可能想要像这样使用它:

IDataService service = ServiceLocator.GetServiceByName("WEB");
service.AddData(...);

可以实现为:

public interface IDataService 
{
    void AddData(...);
}

public interface IServiceLocator
{
    IDataService GetServiceByName(string name);
}

public class ServiceLocator : IServiceLocator
{
    readonly Dictionary<string, Func<IDataService>> _typeFactories = 
        new Dictionary<string, Func<IDataService>>();

    public ServiceLocator()
    {
        _typeFactories["WEB"] = () => new SomeWebService();
        _typeFactories["VIDEO"] = () => new SomeVideoService();
        _typeFactories["VOICE"] = () => SomeVoiceService.Instance; // singleton            
    }

    public IDataService GetServiceByName(string name)
    {
        var factory = _typeFactories[name];
        return factory();
    }
}

所以,经验法则是:

  1. 如果您知道要在编译时使用的服务的界面,请使用通用工厂(即Locator.GetService<ICanDoSomething>())。

  2. 如果多个服务共享完全相同的接口,并且您需要在运行时区分它们,请使用带有类型参数的工厂(即Locator.GetServiceByName("some-service"))。

  3. 这两种方法都不能保证此服务在运行时存在,但后者更容易出现运行时错误(错误输入服务名称)。两种方法在弱耦合方面都不如纯DI,而在硬编码方面优于其他方法:

    // bad
    public SomeClassConstructor()
    {
        _service = new WebService();
    }
    
    // better
    public SomeClassConstructor(IServiceLocator serviceLocator)
    {
        _service = serviceLocator.GetService<IWebService>();
    }
    
    // best
    public SomeClassConstructor(IWebService webService)
    {
        _service = webService;
    }
    

    我建议采用第三种方法。了解composition roots,您通常不需要花哨的DI框架。