Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/api/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 设计API以接受它生成的接口引用 概述_C#_Api_Interface_Abstraction - Fatal编程技术网

C# 设计API以接受它生成的接口引用 概述

C# 设计API以接受它生成的接口引用 概述,c#,api,interface,abstraction,C#,Api,Interface,Abstraction,我正在开发一个应用程序,它使用两个主要的抽象级别: 核心库定义了许多接口,并包含实现接口核心功能的类。通过这种方式,我希望只编写一次核心算法,但使它们适用于任何数量的接口实现 “实现”库使用第三方SDK提供一组接口的特定实现。最终将有不止一个这样的图书馆;使用哪一个将由配置决定 应用程序本身实例化SDK库中的类,并使用它们来满足核心库的依赖关系 问题 我需要解决的问题通常如下所示: // Algorithm in the core (interfaces are all implemente

我正在开发一个应用程序,它使用两个主要的抽象级别:

  • 核心库定义了许多接口,并包含实现接口核心功能的类。通过这种方式,我希望只编写一次核心算法,但使它们适用于任何数量的接口实现
  • “实现”库使用第三方SDK提供一组接口的特定实现。最终将有不止一个这样的图书馆;使用哪一个将由配置决定
应用程序本身实例化SDK库中的类,并使用它们来满足核心库的依赖关系

问题 我需要解决的问题通常如下所示:

// Algorithm in the core (interfaces are all implemented by the SDK library):
ICorrespondentRepository allCorrespondents = ...;
ICorrespondent correspondent = allCorrespondents.FindByName(...);
...
IDocumentRepository allDocuments = ...;
IDocument document = allDocuments.FindByTitle(...);

// Problem: Implementation needs state not exposed
// on ICorrespondent in order to do this:
document.SetRecipient(correspondent);
换句话说,
i文档
可以将其收件人设置为以前获得的
i响应
。调用
SetRecipient
时,
IDocument
的实现需要与
ICorrespondent
关联但未公开的状态(对核心不重要的主键),以便实际实现更改

一种方法是向下转换
ICorrespondent
SetRecipient
中的实际实现类,但这感觉非常笨拙。更糟糕的是保持从接口引用到内部状态的映射

问题的根源似乎在于接口的设计完全是为了满足核心库的一般需求,尽管它们实际上有两个具有不同需求的消费者:核心和产生它们的实现库


有没有更好的方法来重新设计这种需求?

实际上,您想要做的是交叉转换

您说,
IDocument
的特定实现与
ICorrespondent
的所有子类都不兼容,因此如果传递的
ICorrespondent
实例没有此主键,您对
SetRecipient
的调用可能会合法地失败。拥有这个主键是兼容子类的一个“特征”

你可以使用的技术就是这个。定义一个接口

internal interface IHasPrimaryKey {
    PrimaryKey GetPrimaryKey();
}
您的兼容
ICorrespondent
类应该实现这两个接口

internal class CompatibleCorrespondent : ICorrespondent, IHasPrimaryKey {
    // ...
}
在这种情况下,
SetRecipient
应该尝试对通信对象进行交叉转换,以查看它是否提供了必要的主键,否则将失败

var hasPrimaryKey = correspondent as IHasPrimaryKey;
if(hasPrimaryKey == null) {
    throw new InappropriateSubclassException();
}
// ...
var pk = hasPrimaryKey.GetPrimaryKey();
这是从这种体系结构中可以得到的最强类型的解决方案。失败案例是合法的,因为typesystem不能保证您在任何情况下都能获得主键

这样做的好处是您不必绑定到特定的子类。任何同时实现
ICorrespondent
IHasPrimaryKey
的类都是合适的


当然,我会让您为特定代码找到更合适的名称。

实际上,您要做的是交叉转换

您说,
IDocument
的特定实现与
ICorrespondent
的所有子类都不兼容,因此如果传递的
ICorrespondent
实例没有此主键,您对
SetRecipient
的调用可能会合法地失败。拥有这个主键是兼容子类的一个“特征”

你可以使用的技术就是这个。定义一个接口

internal interface IHasPrimaryKey {
    PrimaryKey GetPrimaryKey();
}
您的兼容
ICorrespondent
类应该实现这两个接口

internal class CompatibleCorrespondent : ICorrespondent, IHasPrimaryKey {
    // ...
}
在这种情况下,
SetRecipient
应该尝试对通信对象进行交叉转换,以查看它是否提供了必要的主键,否则将失败

var hasPrimaryKey = correspondent as IHasPrimaryKey;
if(hasPrimaryKey == null) {
    throw new InappropriateSubclassException();
}
// ...
var pk = hasPrimaryKey.GetPrimaryKey();
这是从这种体系结构中可以得到的最强类型的解决方案。失败案例是合法的,因为typesystem不能保证您在任何情况下都能获得主键

这样做的好处是您不必绑定到特定的子类。任何同时实现
ICorrespondent
IHasPrimaryKey
的类都是合适的


当然,我会让您为您的特定代码找到更合适的名称。

您不能在
ICorrespondent
公开的其他类型的接口中总结该状态吗?我想你还是得在某个地方沮丧,但这是并行抽象的代价。@siride谢谢你的建议。我考虑过这一点,但考虑到状态是特定于实现的,它似乎相当于向下转换到类。它仍然会将外部持久引用的问题分离到它自己的接口中。@siride按照您建议的方式实现了它,我可以看到这样做的好处。将状态封装在一个(内部)接口中有助于描述例如
SetRecipient
方法的职责,相反,向下转换到实现类将使方法能够访问许多它不需要的东西。您不能将该状态包装到由
ICorrespondent
公开的某种类型的其他接口中吗?我想你还是得在某个地方沮丧,但这是并行抽象的代价。@siride谢谢你的建议。我考虑过这一点,但考虑到状态是特定于实现的,它似乎相当于向下转换到类。它仍然会将外部持久引用的问题分离到它自己的接口中。@siride按照您建议的方式实现了它,我可以看到这样做的好处。将状态封装在(内部)接口中有助于描述例如
SetRecipient
方法的职责,而向下转换到实现类将使该方法能够访问许多它不需要的东西。感谢您的回答!这正是我最终要做的