如何在C#中管理非托管DLL调用的多个版本的使用?

如何在C#中管理非托管DLL调用的多个版本的使用?,c#,dll,unmanaged,dllimport,multiple-versions,C#,Dll,Unmanaged,Dllimport,Multiple Versions,我不知道是否有人能给我指出正确的方向我对c#还相当陌生,所以对我宽容点 我的代码使用了一个非托管DLL,它是一个提供的API,用于与智能卡读卡器接口,我没有对DLL的控制权,我只是想用c#将其包装起来,使其更易于使用。到目前为止,我已经设法做到了这一点,但我发现现在需要测试这个DLL的多个版本,它的入口点相同,但参数可能不同 我尝试了一些类似的方法来组织我的包装器类 internal static class UnSafeNativeMethods { internal static c

我不知道是否有人能给我指出正确的方向我对c#还相当陌生,所以对我宽容点

我的代码使用了一个非托管DLL,它是一个提供的API,用于与智能卡读卡器接口,我没有对DLL的控制权,我只是想用c#将其包装起来,使其更易于使用。到目前为止,我已经设法做到了这一点,但我发现现在需要测试这个DLL的多个版本,它的入口点相同,但参数可能不同

我尝试了一些类似的方法来组织我的包装器类

internal static class UnSafeNativeMethods
{
    internal static class READER
    {
        internal static class SDK20
        {
            [DllImport(dllpath, EntryPoint = "CV_SetCommunicationType")]
            internal static extern int CV_SetCommunicationType(byte type);
            ...
        }

        internal static class SDK21
        {
            [DllImport(dllpath, EntryPoint = "CV_SetCommunicationType")]
            internal static extern int CV_SetCommunicationType(byte type);
            ...
        }
    }
}
但在检查要使用的调用时,它会产生非常难看的代码

ReaderSDK sdk = ReaderSDK.SDK20  //Could come from a argument passed in or set in an 
                                 //instantiated class
...
switch (sdk)
{
    case ReaderSDK.SDK20:
        UnSafeNativeMethods.READER.SDK20.CV_SetCommunicationType(0x0);
        break;
    case ReaderSDK.SDK21:
        UnSafeNativeMethods.READER.SDK21.CV_SetCommunicationType(0x0);
        break;
    ...
}
这对我来说似乎很混乱,不知道是否有人能给我指出正确的方向


编辑:在下面的评论中,我提出了一些示例代码,但仍然不确定我是否在正确的轨道上,因为我的开关仍然存在,但它现在是一个具体的Factory类的一部分

public enum ConnectionType
{
    RS232 = 0x0,
    USB = 0x1,
    UDP = 0x2
}

interface INativeMethods
{
    string Name();
    void SetCommunicationType(ConnectionType type);
}

class SDK20 : INativeMethods
{
    public string Name()
    {
        return "SDK Version 2.0";
    }

    // Thanks to @dzendras for this!!
    public void SetCommunicationType(ConnectionType type)
    {
        int result = UnSafeNativeMethods.READER.SDK20.CV_SetCommunicationType((byte)type);
        switch (result)
        {
            case 0:
                return;
            case 1:
                throw new NotSupportedException("Communication type not supported");
            case 2:
                throw AnyOtherMeaningfulException("Its message");
        }
    }


}

class SDK21 : INativeMethods
{
    public string Name()
    {
        return "SDK Version 2.1";
    }

    // Thanks to @dzendras for this!!
    public void SetCommunicationType(ConnectionType type)
    {
        int result = UnSafeNativeMethods.READER.SDK21.CV_SetCommunicationType((byte)type);
        switch (result)
        {
            case 0:
                return;
            case 1:
                throw new NotSupportedException("Communication type not supported");
            case 2:
                throw AnyOtherMeaningfulException("Its message");
        }
    }
}

class NativeMethodsFactory
{
    private static NativeMethodsFactory instance = new NativeMethodsFactory();
    private NativeMethodsFactory()
    {

    }

    public static NativeMethodsFactory Instance
    {
         get { return NativeMethodsFactory.instance; }
    }
    public INativeMethods Get(ReaderSDK version)
    {
        switch (version)
        {
            case ReaderSDK.SDK20:
                return new SDK20();

            case ReaderSDK.SDK21:
                return new SDK21();

            default:
                return new SDK20();
        }
    }
}
我做得对吗

这就是我现在如何实现对SDK的调用

// sdk passed in as enum, NativeMethods stored as class member.
NativeMethods = NativeMethodsFactory.Instance.Get(sdk);
...
NativeMethods.SetCommunicationType(ConnectionType.USB);
使用模式策略

有关模式的一些链接:

在工厂模式的帮助下,如何使用战略模式进行最终决策的一些示例

INativeMethods nativeMethods = NativeMethodsFactory.Get(UnsafeSdkVersion.V1);
nativeMethods.CV_SetCommunicationType(aType);
优点:

  • 通过接口和工厂实现解耦
  • 没有开关
  • 易于添加新版本,所有其他代码都独立于哪个版本使用

  • 我建议更改托管API。调用C代码(从DLL)不应该强迫您使用结构化范例。你的包装应该是完全客观的。例如:

    internal static extern int CV_SetCommunicationType(byte aType);
    
  • 在成功的情况下,返回的int是0,我想任何其他表示错误的值。返回void并在内部将代码从DLL调用转换为异常

    public void SetCommunicationType(byte type) 
    {
         int result = CV_SetCommunicationType(type);
         switch (result)
         {
             case 0:
                 return;
             case 1:
                 throw new NotSupportedException("Communication type not supported");
             case 2:
                 throw AnyOtherMeaningfulException("Its message");
         }
     }
    
  • 为aType创建枚举

  • 寻找课程。静态方法纯粹是邪恶的。分析您的领域并寻找对象和行为。例如,这可能是(愚蠢的名字,我知道…)ConnectionManager的方法

  • 依靠合同。让您的类(针对不同的SDK)实现一些通用接口

  • 不要以示例方法中所示的方式使用


  • 在我看来,每次API调用都必须使用switch语句是非常不整洁的,我的问题是如何整理我的代码,所以我存储了一个引用,比如说类类型,然后从那里调用非托管调用,但我不确定如何做到这一点。尝试使用singleton类,但问题太多。但我是说c#。在C#上实现模式策略。您将有两种策略-对于每个版本,您还可以避免不必要的切换。哪个版本的execute可以存储在app.configI在C#-中找到了一个关于抽象工厂模式实现的例子。你向我展示了部分谜题,看起来很有希望,但在你的例子中我似乎缺少关键步骤,我如何从NativeMethodFactory到SDK实例?NativeMethodsFactory是静态类吗?,当我遇到无法从静态类返回实例成员的问题时,我已经看到了变化。是的,这和我所说的变化非常相似。和Factory的主要区别在于,您将只在一个位置进行切换,而所有其他代码都将依赖于接口。并执行相同的方法,但根据需要使用不同的实现。NativeMethodsFactory可以是静态的,也可以不是静态的。根据您的意愿和需要实施工厂。工厂的主要目标是基于版本构造对象。只有工厂应该知道具体的实现,所有其他代码都应该使用工厂和接口。谢谢dzendras,这非常有帮助。我通常不使用匈牙利符号,但在本例中,我从未改变SDK示例附带的声明(有很多声明)。这如何帮助我包装多个版本的调用?我知道我遗漏了什么你能帮我把这些点连起来吗?你能在3号上展开吗。寻找类…将返回值设置为异常将是一项艰巨的工作,返回的值是一个字节,而且有很多,但我明白你的意思,谢谢。你可以随时对我的答案进行投票:)你提出的解决方案很好。但是,如果API发生更改(某些参数或新方法),您的代码将降级。。。没有更广泛的背景,我找不到任何解决方案。