Ubuntu中使用C#调用Fortran编译so文件
环境说明:
- Ubuntu版本:v22.04 LTS
- .NET版本:v8.0.110
- GFortran版本:v11.4.0
安装Fortran编译器
在Ubuntu上安装Fortran编译器:
# 更新包列表
sudo apt update
# 安装gfortran编译器
sudo apt install gfortran
创建.NET控制台项目
创建.NET控制台程序,首先创建一个新的.NET项目:
# 创建新的控制台项目
dotnet new console -n FortranTest
cd FortranTest
项目文件结构如下:
FortranTest/
├── fortran/
│ ├── calc.f90
├── Program.cs
├── FortranTest.csproj
└── README.md
// FortranTest.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Include="libcalc.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
// Program.cs
using System.Runtime.InteropServices;
class Program
{
// 导入Fortran函数
[DllImport("libcalc.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void array_sum(
[In] double[] arr,
int size,
out double result);
[DllImport("libcalc.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void array_average(
[In] double[] arr,
int size,
out double result);
static void Main(string[] args)
{
try
{
// 测试数据
double[] testArray = { 1.0, 2.0, 3.0, 4.0, 5.0 };
// 计算总和
double sum = 0.0;
array_sum(testArray, testArray.Length, out sum);
Console.WriteLine($"数组总和: {sum}");
// 计算平均值
double average = 0.0;
array_average(testArray, testArray.Length, out average);
Console.WriteLine($"数组平均值: {average}");
}
catch (Exception ex)
{
Console.WriteLine($"错误: {ex.Message}");
}
}
}
// calc.f90
module calculation
use iso_c_binding
implicit none
contains
! 一个简单的数组求和函数
subroutine array_sum(arr, size, result) bind(c, name='array_sum')
use iso_c_binding
implicit none
integer(c_int), value :: size
real(c_double), intent(in) :: arr(size)
real(c_double), intent(out) :: result
integer :: i
result = 0.0d0
do i = 1, size
result = result + arr(i)
end do
end subroutine array_sum
! 一个简单的数组平均值函数
subroutine array_average(arr, size, result) bind(c, name='array_average')
use iso_c_binding
implicit none
integer(c_int), value :: size
real(c_double), intent(in) :: arr(size)
real(c_double), intent(out) :: result
call array_sum(arr, size, result)
result = result / size
end subroutine array_average
end module calculation
# 编译Fortran代码
cd fortran
gfortran -c -fPIC calc.f90 -o calc.o
gfortran -shared calc.o -o libcalc.so
# 复制.so文件到.NET项目目录
cp libcalc.so ../
# 编译和运行.NET程序
cd ..
dotnet build
dotnet run
能否不复制so文件到项目目录?
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Include="fortran/libcalc.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
此时,build项目后,会在输出目录新增文件“FortranTest/bin/Debug/net8.0/fortran/libcalc.so”,修改代码如下,同样可以正常运行项目
// 导入Fortran函数
[DllImport("fortran/libcalc.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void array_sum(
[In] double[] arr,
int size,
out double result);
[DllImport("fortran/libcalc.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void array_average(
[In] double[] arr,
int size,
out double result);
不建议直接在DllImport中使用相对路径。这可能会导致运行时路径解析问题。为了更可靠的路径解析,可以修改代码如下:
using System.Runtime.InteropServices;
class Program
{
// 导入Fortran函数
[DllImport("libcalc.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void array_sum(
[In] double[] arr,
int size,
out double result);
[DllImport("libcalc.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void array_average(
[In] double[] arr,
int size,
out double result);
static void Main(string[] args)
{
try
{
// 添加库文件搜索路径解析器
NativeLibrary.SetDllImportResolver(typeof(Program).Assembly, (libraryName, assembly, searchPath) =>
{
// 获取程序运行目录下的 fortran 子目录
string libraryPath = Path.Combine(AppContext.BaseDirectory, "fortran", libraryName);
return NativeLibrary.Load(libraryPath);
});
// 测试数据
double[] testArray = { 1.0, 2.0, 3.0, 4.0, 5.0 };
// 计算总和
double sum = 0.0;
array_sum(testArray, testArray.Length, out sum);
Console.WriteLine($"数组总和: {sum}");
// 计算平均值
double average = 0.0;
array_average(testArray, testArray.Length, out average);
Console.WriteLine($"数组平均值: {average}");
}
catch (Exception ex)
{
Console.WriteLine($"错误: {ex.Message}");
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!