如何将跨平台.NET Standard/.NET Core库封装为Ruby Gem?
Great question! Since you want to keep your core .NET logic intact while making it accessible to Ruby developers, here are the most practical tools and patterns to pull this off:
1. Ruby FFI + .NET Native/Shared Libraries
This is the most lightweight, cross-platform approach for direct function calls between Ruby and .NET.
Tools you'll need:
- Ruby's
ffigem (the standard way to call native code from Ruby) - .NET 6+ (to compile your library into a native/shared library)
How it works:
- Prepare your .NET library: Mark the methods you want Ruby to access with the
[UnmanagedCallersOnly(EntryPoint = "YourFunctionName")]attribute. This exposes them as C-style functions that can be called from native code.using System.Runtime.InteropServices; public class MyDotNetLibrary { [UnmanagedCallersOnly(EntryPoint = "AddNumbers")] public static int Add(int a, int b) { return a + b; } [UnmanagedCallersOnly(EntryPoint = "GetGreeting")] public static IntPtr GetGreeting() { // Return a UTF-8 string that Ruby can free later return Marshal.StringToHGlobalUTF8("Hello from .NET!"); } } - Compile .NET to native/shared library:
- For cross-platform native binaries (no .NET Runtime required), use Native AOT:
dotnet publish -r linux-x64 -c Release /p:PublishAot=true dotnet publish -r osx-x64 -c Release /p:PublishAot=true dotnet publish -r win-x64 -c Release /p:PublishAot=true - If you don't mind requiring users to have the .NET Runtime installed, publish as a shared library:
dotnet publish -r linux-x64 -c Release --self-contained false
- For cross-platform native binaries (no .NET Runtime required), use Native AOT:
- Write Ruby bindings with FFI:
require 'ffi' module MyDotNetLib extend FFI::Library # Load the correct library based on the OS lib_path = case RbConfig::CONFIG['host_os'] when /linux/ then './linux-x64/MyDotNetLibrary.so' when /darwin/ then './osx-x64/MyDotNetLibrary.dylib' when /mswin|mingw/ then './win-x64/MyDotNetLibrary.dll' end ffi_lib lib_path # Bind the exposed .NET functions attach_function :AddNumbers, [:int, :int], :int attach_function :GetGreeting, [], :pointer def self.get_greeting ptr = GetGreeting() str = ptr.read_string_utf8 FFI::MemoryPointer.new(ptr).free # Clean up the .NET-allocated string str end end # Use it in Ruby puts MyDotNetLib.AddNumbers(2, 3) # Outputs 5 puts MyDotNetLib.get_greeting # Outputs "Hello from .NET!"
Pros & Cons:
- ✅ Cross-platform support (works on Linux, macOS, Windows)
- ✅ No extra runtime overhead beyond FFI
- ❌ Manual type conversion required (especially for complex objects; serialize to JSON strings if needed)
- ❌ Need to handle memory management for shared resources
2. gRPC for Cross-Language Service Calls
If your .NET library has a complex API with objects and workflows, gRPC is a robust, standardized way to expose it to Ruby (and other languages).
Tools you'll need:
- gRPC for .NET (to build the service server)
- gRPC for Ruby (to generate client stubs)
- Protocol Buffers (
protoccompiler)
How it works:
- Define your API with Protocol Buffers: Write a
.protofile that describes your service methods and data structures.syntax = "proto3"; package mydotnetlib; service Calculator { rpc Add(AddRequest) returns (AddResponse); rpc GetGreeting(EmptyRequest) returns (GreetingResponse); } message AddRequest { int32 a = 1; int32 b = 2; } message AddResponse { int32 result = 1; } message EmptyRequest {} message GreetingResponse { string message = 1; } - Implement the gRPC server in .NET: Use the .NET gRPC SDK to write a server that executes your core .NET logic when called.
- Generate Ruby client code: Use
protocwith the Ruby plugin to generate client stubs from your.protofile. - Run the .NET server and call it from Ruby: Ruby developers can use the generated client to connect to your .NET server (local or remote) and invoke methods.
Pros & Cons:
- ✅ Handles complex objects and serialization automatically
- ✅ Supports advanced features like streaming, authentication, and error handling
- ✅ Works across all your target platforms
- ❌ Requires running a separate .NET server process (can be local or containerized)
- ❌ Adds network overhead (even for localhost calls)
3. COM Interop (Windows-Only)
If you only need to support Windows users, COM Interop lets Ruby access your .NET library as a COM object.
Tools you'll need:
- .NET library configured for COM visibility
- Ruby's
win32olegem
How it works:
- Enable COM visibility in your .NET project: Set
[ComVisible(true)]on your classes/methods, and configure the project to generate a COM interop assembly. - Register the .NET library as a COM component: Use
regasm.exeto register the library on Windows. - Call the COM object from Ruby:
require 'win32ole' dotnet_lib = WIN32OLE.new('MyDotNetLibrary.MyDotNetClass') puts dotnet_lib.AddNumbers(2, 3) # Outputs 5 puts dotnet_lib.GetGreeting() # Outputs "Hello from .NET!"
Pros & Cons:
- ✅ Seamless OOP integration on Windows
- ✅ No manual type conversion needed
- ❌ Windows-only (doesn't work on Linux/macOS)
- ❌ Requires COM registration, which can be cumbersome for deployment
4. Package as a RubyGem for Easy Distribution
Whichever approach you choose, wrap everything into a RubyGem to make installation trivial for Ruby developers.
- Include pre-compiled .NET shared libraries (for FFI) or gRPC client stubs in your gem's
libdirectory. - Use platform-specific logic in your gem to load the correct .NET library (like the OS check in the FFI example above).
- Write a clean Ruby API wrapper that hides the low-level details from users.
Example gemspec snippet:
Gem::Specification.new do |spec| spec.name = 'my_dotnet_lib_ruby' spec.version = '0.1.0' spec.files += Dir['lib/**/*', 'ext/linux-x64/*.so', 'ext/osx-x64/*.dylib', 'ext/win-x64/*.dll'] end
Final Tips:
- For FFI, simplify cross-language communication by serializing complex objects to JSON strings instead of passing raw pointers.
- Test your gem on all target platforms (Linux, macOS, Windows) to catch compatibility issues early.
- If using Native AOT, be aware of its limitations (e.g., no dynamic code generation, limited .NET API support).
内容的提问来源于stack exchange,提问作者Phobis
相关产品推荐
相关产品推荐

