龙空技术网

C#与C++交互开发系列之函数参数传递之值传递

dotnet研习社 218

前言:

如今咱们对“vb参数传递的两种方式”大约比较珍视,朋友们都想要剖析一些“vb参数传递的两种方式”的相关内容。那么小编也在网摘上汇集了一些对于“vb参数传递的两种方式””的相关知识,希望姐妹们能喜欢,兄弟们一起来学习一下吧!

前言

很多时候,我们在进行桌面软件程序开发的时候,会遇到C#调用C++开发的动态库。那么必然要面对函数传递参数的各种问题。今天我们就开启这个系列的学习和研究,详细解读如何交互。在学习C#调用的时候,也了解C++代码关于函数的声明,参数的定义,数据的解析,指针的使用,数据的托管内存和非托管内存之间的拷贝和内存释放等等内容。下面我们来一起熟悉:C#与C++交互开发系列之函数参数传递之值传递。

C++部分

1、新增C++动态库,我们命名为CppLibrary

2、通过类向导,我们添加类SimpleMath文件

3、用来承载我们最简单的加减乘除的四则运算。

下面是SimpleMath.h的内容:

#pragma once#ifndef SIMPLE_MATH_H#define SIMPLE_MATH_H#define API extern "C" __declspec(dllexport)API double add(double a, double b);API double subtract(double a, double b);API double multiply(double a, double b);API double divide(double a, double b);#endif // SIMPLE_MATH_H

下面是SimpleMath.cpp

#include "pch.h"#include "SimpleMath.h"double add(double a, double b){	return a + b;}double subtract(double a, double b){	return a - b;}double multiply(double a, double b) {	return a * b;}double divide(double a, double b) {	if (b == 0) {		return 0; 	}	return a / b;}

这里简单的解释下,在SimpleMath.h中的内容 SimpleMath.cpp实现比较容易理解:

1. `#pragma once`:这是一个预处理指令,用于确保头文件只被编译一次。当编译器遇到 `#pragma once` 时,会将当前文件标记为已包含,并在后续引用该文件时跳过再次包含。2. `#ifndef SIMPLE_MATH_H` 和 `#define SIMPLE_MATH_H`:这是常用的防止头文件重复包含的标准方法。`#ifndef` 检查一个标识符是否未定义,如果未定义,则继续处理后续代码。`#define` 定义了这个标识符,用于防止重复包含。这种组合的作用是,如果 `SIMPLE_MATH_H` 这个标识符未定义(即第一次包含该头文件),则会继续处理后续代码,同时定义了 `SIMPLE_MATH_H` 标识符,防止下次重复包含。3. `#define API extern "C" __declspec(dllexport)`:这是一个宏定义,用于声明函数的外部可见性。`extern "C"` 用于告诉编译器按照 C 语言的约定进行函数名称的命名,而 `__declspec(dllexport)` 则用于指示这些函数将被导出到动态链接库中,以便其他程序可以使用这些函数。4. 声明了四个数学运算函数 `add`、`subtract`、`multiply` 和 `divide`,这些函数将在其他文件中定义实现。5. `#endif`:这是条件编译的结束标记,表示条件编译块的结束。C#部分

1、我们新增控制台项目,命名为CSharpApp

2、声明一个包装类CppLibraryWapper,用来封装对CppLibrary库的引用,之所以要这样封装,是出于集成化思维的考虑,便于统一管理。在这里,我们使用P/Invoke技术:

namespace CSharpApp{    public static class CppLibraryWapper    {        [DllImport("CppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]        public static extern double add(double a, double b);        [DllImport("CppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]        public static extern double subtract(double a, double b);        [DllImport("CppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]        public static extern double multiply(double a, double b);        [DllImport("CppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]        public static extern double divide(double a, double b);    }}

P/Invoke(Platform Invocation Services)是.NET提供的一项功能,用于在托管代码(如C#或VB.NET)中调用非托管代码(如C或C++)的功能。它允许.NET应用程序访问和调用系统级API、动态链接库(DLL)或共享对象(SO)中的函数。

3、编写Program代码:

static void Main(string[] args){    double a = 100;    double b = 10;    Console.WriteLine($"Add = {CppLibraryWapper.add(a, b)}");    Console.WriteLine($"subtract = {CppLibraryWapper.subtract(a, b)}");    Console.WriteLine($"multiply = {CppLibraryWapper.multiply(a, b)}");    Console.WriteLine($"Divide = {CppLibraryWapper.divide(a, b)}");    Console.ReadLine();}

4、运行试试

这个时候,我们发现报错了,System.DllNotFoundException:“Unable to load DLL 'CppLibrary.dll' or one of its dependencies: 找不到指定的模块。 (0x8007007E)”。找不到CppLibrary.dllDLL文件。

这个错误也是比较常见的一个错误。这个时候我们的解决办法就是。调整CppLibrary项目的输出目录为。

$(SolutionDir)CSharpApp\bin\$(Configuration)\net8.0

应用生效后,重新编译CppLibrary项目。

5、再一次遇到bug

再一次尝试运行。发现又出现了新的问题。无法找到Divide方法。原来包装类中使用的函数名,于CppLibrary库中不一致。由此说明,函数名必须一致。严格区分大小写。

修改完毕后,我们继续执行。

这一次,我们成功执行完毕,获得所预计的结果。

关于值传递

前期我们的内容搭建完毕,现在我们在C++项目中新增一个方法:

# SimpleMath.hAPI void modifyValue(double value);# SimpleMath.cppvoid modifyValue(double value){	value += 10;}

我们在C#项目中写入调用代码

[DllImport("CppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]public static extern void modifyValue(double a);Console.WriteLine($"a = {a}");CppLibraryWapper.modifyValue(a);Console.WriteLine($"a = {a}");

运行起来,我们会发现,输出的结果没有变化。

思考下,这是为什么呢?原来是因为:

在 C# 中,参数传递默认情况下是值传递。这意味着当你调用一个方法时,传递给方法的是实参的值的副本,而不是实参本身。在方法内对参数的任何更改都不会影响到原始值。虽然在 ModifyValue 方法内部将 value 修改为110.在 C++ 中,函数参数默认也是通过值传递的。在函数调用时,实参的值被复制到形参中,函数内部的任何修改都只会影响形参的值,而不会影响到原始实参。

总结

无论是在 C# 还是在 C++ 中,默认情况下,函数参数都是通过值传递的。这意味着在函数内部对参数的任何更改都不会影响到原始值。如果想要函数内部对参数的修改能够影响到原始值,可以考虑使用什么方法呢?这个问题,我们一下篇文章继续分析。

如果本文对你有帮助,我将非常荣幸。

如果你对P/Invoke参数传递有自己的见解,欢迎留言交流。

如果你喜欢我的文章,谢谢三连,点赞,关注,转发吧!!

#头条创作挑战赛# #编程经验# #记录我的2024#

标签: #vb参数传递的两种方式