如何使 C 中的方法链流畅?

2023-12-01

现有的 C API 如下所示:

//data
typedef struct {int properties;} Widget;

//interface
Widget* SetWidth(Widget *const w, int width){
    // ...
    return w;
}
Widget* SetHeight(Widget *const w, int height){
    // ...
    return w;
}
Widget* SetTitle(Widget *const w, char* title){
    // ...
    return w;
}
Widget* SetPosition(Widget *const w, int x, int y){
    // ...
    return w;
}

第一个参数始终是指向实例的指针,并且转换实例的函数始终将其作为指针返回。

我认为这样做是为了支持某种方法链接?

当函数作为方法存在于对象范围内时,方法链接在语言中才有意义。考虑到 API 的当前状态,我只能这样使用它:

int main(void) {
    Widget w;
    SetPosition(SetTitle(SetHeight(SetWidth(&w,400),600),"title"),0,0);
}

我可以在 C 语言中使用任何技术来获得与其他语言相同的流畅性吗?


C中没有任何语法技巧可以实现方法链接正如在其他一些语言中可能使用的那样。在 C 中,您可以编写单独的函数调用,将对象指针传递给每个函数:

Widget *w = getWidget();
widgetSetWidth(w, 640);
widgetSetHeight(w, 480);
widgetSetTitle(w, "Sample widget");
widgetSetPosition(w, 0, 0);

对于 C++ 和其他 OOP 语言中的方法调用也可以完成同样的操作:

Widget *w = getWidget();
w->SetWidth(640);
w->SetHeight(480);
w->SetTitle("Sample widget");
w->SetPosition(0, 0);

使用上述 API,并假设每个方法返回this对象,方法链接语法如下所示:

getWidget()->SetWidth(640)->SetHeight(480)->SetTitle("Sample widget")->SetPosition(0, 0);

这是否比单独的语句更具可读性取决于品味和本地编码约定。我个人觉得它很麻烦,更难读。在代码生成方面有一个小优点:下次调用时不需要从局部变量重新加载对象指针。这种微小的优化很难证明链接语法是合理的。

一些程序员尝试通过这种方式让它变得更容易接受:

getWidget()
 -> SetWidth(640)
 -> SetHeight(480)
 -> SetTitle("Sample widget")
 -> SetPosition(0, 0);

再说一次,这是一个品味和编码约定的问题……但是 C 语言的等价物看起来确实很尴尬:

Widget *w = widgetSetPosition(widgetSetTitle(widgetSetHeight(widgetSetWidth(getWidget(), 640), 480), "Sample widget"), 0, 0);

并且没有简单的方法可以将这个链重新组织成更具可读性。

请注意,一些最古老的 C 库函数可以链接到:

const char *hello = "Hello";
const char *world = "World";
char buf[200];
strcpy(buf, hello);
strcat(buf, " ");
strcat(buf, world);
strcat(buf, "\n");

可以重组为:

strcat(strcat(strcat(strcpy(buf, hello), " "), world), "\n");

但更安全且更受青睐的方法是:

snprintf(buf, sizeof buf, "%s %s\n", hello, world);

欲了解更多信息,您可能想阅读以下内容:

Marco Pivetta (Ocramius):流畅的界面是邪恶的

另请注意,如果 C 对象具有用于这些调用的函数指针成员,则可以使用上述所有语法,但仍必须将对象指针作为参数传递。函数指针通常分组在对象中存储指针的结构中,模仿 C++ 虚拟方法的实现,使语法稍微重一些:

Widget *w = getWidget();
w->m->SetWidth(w, 640);
w->m->SetHeight(w, 480);
w->m->SetTitle(w, "Sample widget");
w->m->SetPosition(w, 0, 0);

将它们链接起来也是可能的,但没有真正的好处。

最后,应该注意的是,方法链接不允许显式的错误传播。在 OOP 语言中,链接是惯用的,可以抛出异常以或多或少令人满意的方式发出错误信号。在 C 中,处理错误的惯用方法是返回错误状态,这与返回指向对象的指针相冲突。

因此,除非保证方法成功,否则建议不要使用方法链并执行迭代测试:

Widget *w = getWidget();
if (SetWidth(w, 640)
||  SetHeight(w, 480)
||  SetTitle(w, "Sample widget")
||  SetPosition(w, 0, 0)) {
    /* there was an error, handle it gracefully */
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使 C 中的方法链流畅? 的相关文章

  • C# SmtpClient编程中如何设置带有中文的附件文件名?

    我的代码如下 ContentType ct new ContentType ct MediaType MediaTypeNames Application Octet ct Name 这是一个很长的中文文件名希望能用它在附件名中 Doc A
  • 使用 ADAL v3 使用 ClientID 对 Dynamics 365 进行身份验证

    我正在尝试对我们的在线 Dynamics CRM 进行身份验证以使用可用的 API 我能找到的唯一关于执行此操作的官方文档是 https learn microsoft com en us dynamics365 customer enga
  • C#.Net 邮件将进入垃圾邮件文件夹

    我正在从 ASP net Web 应用程序发送电子邮件 邮件发送成功 没有失败 但大多数都进入了垃圾邮件文件夹 请帮助我克服垃圾邮件过滤器 我的发送邮件代码 public void SendMail string FromAddress s
  • EntityHydrate 任务失败

    我最近安装了 Visual Studio 11 Beta 和 Visual Studio 2010 之后 我无法在 Visual Studio 2010 中构建依赖于 PostSharp 的项目 因此我卸载了 Visual Studio 1
  • Boost ASIO 串行写入十六进制值

    我正在使用 ubuntu 通过串行端口与设备进行通信 所有消息都必须是十六进制值 我已经在 Windows 环境中使用白蚁测试了通信设置 并得到了我期望的响应 但在使用 Boost asio 时我无法得到任何响应 以下是我设置串口的方法 b
  • 2个对象,完全相同(除了命名空间)c#

    我正在使用第三方的一组网络服务 但遇到了一个小障碍 在我手动创建将每个属性从源复制到目标的方法之前 我想我应该在这里寻求更好的解决方案 我有 2 个对象 一个是 Customer CustomerParty 类型 另一个是 Appointm
  • 如何修复错误:“检测到无法访问的代码”

    我有以下代码 private string GetAnswer private int CountLeapYears DateTime startDate return count String answer GetAnswer Respo
  • 如何向 Mono.ZeroConf 注册服务?

    我正在尝试测试 ZeroConf 示例http www mono project com Mono Zeroconf http www mono project com Mono Zeroconf 我正在运行 OpenSuse 11 和 M
  • C# 根据当前日期传递日期时间值

    我正在尝试根据 sql server 中的两个日期获取记录 Select from table where CreatedDate between StartDate and EndDate我通过了5 12 2010 and 5 12 20
  • OpenGL:如何检查用户是否支持glGenBuffers()?

    我检查了文档 它说 OpenGL 版本必须至少为 1 5 才能制作glGenBuffers 工作 用户使用的是1 5版本但是函数调用会导致崩溃 这是文档中的错误 还是用户的驱动程序问题 我正在用这个glGenBuffers 对于VBO 我如
  • C# 获取数据表中所有重复行的计数

    我通过运行存储过程来填充数据集 并且从数据集中填充数据表 DataSet RawDataSet DataAccessHelper RunProcedure storedprocedureName this will just return
  • 让网络摄像头在 OpenCV 中工作

    我正在尝试让我的网络摄像头在 Windows 7 64 位中的 OpenCV 版本 2 2 中捕获视频 但是 我遇到了一些困难 OpenCV 附带的示例二进制文件都无法检测到我的网络摄像头 最近我发现这篇文章表明答案在于重新编译一个文件 o
  • 对于 C# Express 用户来说,有哪些好的工具可以识别可能重复的代码? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 也可以看看 有什么工具可以检查重复的 VB NET 代码吗 https stackoverflow c
  • .NET 和 Mono 之间的开发差异

    我正在研究 Mono 和 NET C 将来当项目开发时我们需要在 Linux 服务器上运行代码 此时我一直在研究 ASP NET MVC 和 Mono 我运行 Ubuntu 发行版 想要开发 Web 应用程序 其他一些开发人员使用 Wind
  • Unity3D - 将 UI 对象移动到屏幕中心,同时保持其父子关系

    我有一个 UI 图像 它的父级是 RectTransform 容器 该容器的父级是 UI 面板 而 UI 面板的父级是 Canvas 我希望能够将此 UI 图像移动到屏幕中心 即画布 同时保留父级层次结构 我的目标是将 UI 图像从中心动画
  • 调用 .ToArray() 时出现 ArgumentException

    我有一个经常被清除的列表 代码完全是这样的 VisitorAgent toPersist List
  • 如何编写一个接受 int 或 float 的 C 函数?

    我想用 C 语言创建一个扩展 Python 的函数 该函数可以接受 float 或 int 类型的输入 所以基本上 我想要f 5 and f 5 5 成为可接受的输入 我认为我不能使用if PyArg ParseTuple args i v
  • 从后面的代码添加外部 css 文件

    我有一个 CSS 文件 例如 SomeStyle css 我是否可以将此样式表文档从其代码隐藏应用到 aspx 页面 您可以将文字控件添加到标头控件中 Page Header Controls Add new System Web UI L
  • 声明一个负长度的数组

    当创建负长度数组时 C 中会发生什么 例如 int n 35 int testArray n for int i 0 i lt 10 i testArray i i 1 这段代码将编译 并且启用 Wall 时不会出现警告 并且似乎您可以分配
  • 如何在 C# 中获取 CMD/控制台编码

    我需要指定正确的代码页来使用 zip 库打包文件 正如我所见 我需要指定控制台编码 在我的例子中为 866 C Users User gt mode Status for device CON Lines 300 Columns 130 K

随机推荐

  • 在PYQT5 python中设置文本

    我的程序是基于人工智能的 我在 stackoverflow 中看到了所有关于 Pyqt 设置文本的帖子 我想在单击 自动 AI 按钮时在 lineEdit 中设置文本 def AI click self self lineEdit setT
  • 无法加载捆绑包中的 NIB

    由于未捕获的异常 NSInternalInconsistencyException 而终止应用程序 原因 无法在捆绑包中加载 NIB NSBundle 已加载 名称为 ViewLecturer ViewLecturer viewLectur
  • 使用 Spring Security SAML 将请求参数添加到 SAML 请求

    我需要向 SAML 请求添加请求参数 例如 locale en 以便让登录页面显示正确的语言 我怎么做 我尝试将该属性添加到作为参数发送给开始方法 SamlEntryPoint 的 HttpServletRequest 但这似乎不起作用 有
  • iOS 静态库 + CocoaPods 和重复符号错误

    我正在开发一个静态库 iOS 框架 Jeff Verkoeyen 方式 并且添加了 CocoaPods 来管理依赖项 这是与有关重复符号的其他问题的最大区别 当使用 CocoaPods 的客户端使用我的静态库 框架和我也在使用的第三方库时
  • QSound::play("soundpath") 调用有效,但 QSound 对象无效

    我正在尝试播放声音QSound模块 它尝试使用此代码并工作 QSound play sounds sources BeepSound wav 但我想要这个但不起作用 我创建了一个动态实例QSound并演奏 sounds new QSound
  • java 中的 new Date() 转儒略日期格式

    我需要将 new Date 转换为儒略日期格式 java 中是否有为此构建的函数 我的确切要求是 以儒略日期格式 0YYDDD 表示文件的创建日期 0 数字零 YY 年份的最后两位数 DDD 年内的天数 最多可以是传输日期之前的 7 个日历
  • 如何在 tkinter 中创建日期选择器?

    tkinter 应用程序是否有任何标准方式允许用户选择日期 如果有人仍然需要这个 这里有一个简单的代码 用于使用 tkcalendar 包创建日历和 DateEntry 小部件 pip install tkcalendar 用于安装包 tr
  • 使用 Spring Integration 同时读取 CSV 文件

    我想使用 spring 集成同时处理 CSV 文件 每行将被转换为单独的消息 所以假设我在 CSV 文件中有 10K 行 我想启动 10 个线程 每一行都会传递给这个线程 如果有人向我展示任何示例 那就太好了 Thanks 从 开始Spri
  • 如何编写 Delphi Galileo IDE Expert?

    HI 我想为 D2007 D2009 又名 Galileo IDE 编写一个小型的 Delphi IDE Expert 以便显示一个带有 TMemo 实例的窗口 以及上面备忘录中表单设计器的所有组件名称和类 所选组件将标有 专家应出现在菜单
  • 在 AWS Sagemaker 上安装 graphviz

    我在使用 Python3 的 Jupyter 笔记本上尝试使用如下代码绘制一棵树 import xgboost as xgb from xgboost import plot tree plot tree model num trees 4
  • 如何以编程方式确定 ndb 属性是否为多值

    我正在将应用程序从 Datastore 转换为 ndb 并且在 xml 导入例程中遇到问题 问题是我无法以编程方式确定 ndb model 类的属性是否是多值属性 我怀疑这是由于缺乏基本的 Python 技能 因为到目前为止我编写的代码显示
  • 将 Windows 7 手机的联系人列表集成到应用程序中

    如何获取 Windows 7 手机的联系人列表以在 win7 手机应用程序中使用 对于早期版本的 Windows Phone 7 SDK 只能使用选择器检索电话号码或电子邮件地址以及其他一些信息 现在 使用 7 1 Mango SDK 可以
  • 如何自动将版本号插入AssemblyName

    我试图建立在这个问题的基础上 从 MSBuild 中的文件读取单个值 我的目标是有一个位置来放置多个项目中使用的版本号 并且我还希望其中一个项目的 DLL 文件名中包含版本号的一部分 根据上面的问题 我已经得到了第一部分 但我在第二部分上遇
  • Spring MVC,从请求生成表单支持对象?

    我正在使用 Spring MVC 2 5 并且尝试从 GET 请求加载 JSTL 表单对象 我有 Hibernate POJO 作为我的支持对象 请求中有一个页面指向另一个带有类 id 行主键 的页面 该请求类似于 newpage htm
  • 通过 R 在 SQL 查询中粘贴值

    我有以下包含 AxiomaID 的数据框 x lt c 0123 234 2348 345 3454 并尝试在 R 中运行以下 SQL 查询 SQL6 lt data frame sqlQuery myConn SELECT top 10
  • 创建 bean 'entityManagerFactory' 时出错,嵌套 HibernateException:无法获取默认的 Bean 验证工厂

    关于这个主题已经提出了很多问题 但似乎没有一个能解决我的问题 我尝试使用 Maven Spring Hibernate 和 JPA 以及 Mysql 5 5 构建一个示例项目 这是一个测试桌面应用程序 我不知道 我哪里错了 mvn clea
  • 在php中将数组转换为png [关闭]

    很难说出这里问的是什么 这个问题模棱两可 含糊不清 不完整 过于宽泛或言辞激烈 无法以目前的形式合理回答 如需帮助澄清此问题以便重新打开 访问帮助中心 我想知道如何将颜色数组转换为 png 图像文件 该数组称为 pixels 请帮我 im
  • PhoneGap Android Plugin - 关闭插件 Activity

    我编写了一个 PhoneGap Android 插件 并在那里打开了第二个活动 cordova getActivity runOnUiThread new Runnable Override public void run Context
  • PHP 如何知道何时删除会话?

    我认为会话存储在客户端 因为浏览器关闭时会话会被 删除 然而 今天我被告知这不是真的 会话存储在服务器上 那么会话如何知道浏览器何时关闭 以便会话被删除呢 事实并非如此 有两个因素在起作用 the cookie 的生命周期在客户端 该 co
  • 如何使 C 中的方法链流畅?

    现有的 C API 如下所示 data typedef struct int properties Widget interface Widget SetWidth Widget const w int width return w Wid