TorchSharp wraps LibTorch and lets you skip Python, at the cost of a native backend you must choose
A .NET library that provides access to the library that powers PyTorch.
At a glance
- What is it?
- TorchSharp is the .NET Foundation binding to LibTorch, with a NuGet split between the managed layer and platform-specific native backends. The training loop reads like the PyTorch one, but naming, module constructors and memory disposal all differ in documented ways.
- Who is it for?
- Adopt TorchSharp when you need to train or run LibTorch models inside a .NET service and cannot host a Python process. Do not adopt it expecting code you can copy from Python unchanged, because named arguments and module construction both differ.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A wrapper library and nothing more
TorchSharp is described plainly: a wrapper library, no more, no less. It binds the API LibTorch surfaces, with the emphasis on tensors, and it is part of the .NET Foundation rather than a single company's project.
The training loop is the part most people evaluate it on, and it is deliberately close to PyTorch.
var lin1 = Linear(1000, 100);
var lin2 = Linear(100, 10);
var seq = Sequential(("lin1", lin1), ("relu1", ReLU()), ("drop1", Dropout(0.1)), ("lin2", lin2));
using var x = torch.randn(64, 1000);
using var y = torch.randn(64, 10);
var optimizer = torch.optim.Adam(seq.parameters());
for (int i = 0; i < 10; i++) {
using var eval = seq.forward(x);
using var output = functional.mse_loss(eval, y, Reduction.Sum);
optimizer.zero_grad();
output.backward();
optimizer.step();
}The shape of that loop is the PyTorch shape: build modules, make tensors, make an optimizer over the parameters, then forward, loss, zero_grad, backward, step. Tensor and module lifetimes are handled with using, which is a .NET addition rather than a PyTorch one.
The design intent stated in the README is similarity to the PyTorch experience, with the static type system used where it helps: method overloading stands in for the several valid types PyTorch accepts for a parameter. DiffSharp is cited as a heavy user of the repository and a driver of support for specific APIs.
Five places where C# is not Python, and the library admits it
The README has a section listing what differs, and it is worth reading before writing code rather than after.
First,.NET naming conventions are set aside where they would hurt the Python-parity goal. The rationale given is that examples and snippets should transfer from Python to.NET, following the SciSharp community. If PascalCase bothers you, that is the trade.
Second, every module class has a factory method with the same name, because a method and a class cannot share a name in one scope. The class declarations moved into a nested Modules scope, so a call like `Module conv1 = Conv1d(...)` constructs `Modules.Conv1d`, which derives from torch.Module.
Third, and this one bites hardest in practice: C# uses a colon for named arguments, while Python uses an equals sign. Since PyTorch functions have enough parameters that named arguments are common, a lot of code cannot be copied across unchanged.
Fourth, PyTorch encodes things that are effectively enums as strings in a number of APIs, and TorchSharp uses real.NET enumeration types in most cases. That means a string literal from a Python tutorial will not compile.
Fifth, torch.device is torch.Device, with the constructors still called device() since that is what you call most of the time.
Choosing a backend package is the first real decision
TorchSharp on NuGet is split. The managed library is TorchSharp, and it needs a native backend, which the README recommends you take from the bundled packages so both halves arrive together: TorchSharp-cpu for CPU on Linux, Windows and OSX, TorchSharp-cuda-windows for CPU/CUDA 12.1 on Windows, and TorchSharp-cuda-linux for CPU/CUDA 12.1 on Linux.
If you would rather compose it yourself, the libtorch packages are enumerated: libtorch-cpu-linux-x64, libtorch-cpu-win-x64, libtorch-cpu-osx-arm64, libtorch-cpu which references all three at the cost of a larger download, and libtorch-cuda-12.1-linux-x64 and libtorch-cuda-12.1-win-x64.
The CUDA package has a floor the README calls out: because of the very large native binaries, libtorch-cuda-12.1-linux-x64 requires .NET 6, for example SDK version 6.0.100-preview.5.21302.13 or greater. On a build agent pinned to an older SDK, this is a hard stop rather than a warning.
There is also a path for people who already have LibTorch locally, for example from a Python package manager install of PyTorch. You reference the existing native binaries and load the library explicitly with NativeLibrary.Load, which is how you end up sharing a LibTorch between a Python environment and a .NET process.
Related packages exist alongside: TorchAudio and TorchVision, with their own NuGet entries. Examples moved out to a separate TorchSharpExamples repository.
Intel Macs are gone, and the pre-1.0 API will keep moving
Two constraints shape planning.
The first is hardware. The README states TorchSharp no longer supports macOS on Intel hardware. With libtorch 2.4.0, Intel support was deprecated upstream, and the last TorchSharp version that works on Intel Macintosh hardware is 0.102.8. Starting with 0.103.0, only Apple Silicon Macs are supported. The libtorch package list bears that out: the OSX entry is libtorch-cpu-osx-arm64, with no x64 variant.
The second is the version number itself. The README states that as the project builds toward a v1.0 release, breaking changes will continue, but only where they consider them necessary for usability, and that similarity to the PyTorch experience remains the primary design tenet.
That is a promise of direction rather than of stability, and the 0.102.8 to 0.103.0 boundary is a concrete example of what a breaking change looks like in practice: an entire supported platform removed.
One configuration note that is easy to miss. When targeting .NET Framework on Windows, the project configuration must be set to x64 rather than Any CPU for anything depending on TorchSharp. That is a project property, not a code change, and the failure mode without it is not a clear error message.
For what changed in a given version, the repository points to RELEASENOTES.md, which is where an upgrade should be checked before the bump.
Where TorchSharp is the wrong choice
The obvious alternative is calling Python from .NET: a hosted process, a named pipe, or a service boundary, with PyTorch running natively in its own environment. The difference in approach is real. TorchSharp keeps tensors in-process and gives you static typing, which removes a serialisation boundary and gives you a refactorable API. In exchange you inherit the LibTorch native package matrix, the platform drops, and an API that is pre-1.0.
TorchSharp is the wrong tool when your models are already PyTorch and you need an operation nobody has bound. The library binds what LibTorch surfaces, so an unported function means falling back to interop yourself. The README's framing of no more, no less is the warning: there is no convenience layer to absorb the gap.
It is also the wrong choice when the deployment target cannot carry the native binaries, which is why the Intel Mac limit and the .NET 6 floor on the CUDA package are practical blockers rather than curiosity.
Memory management deserves its own warning. The README links to docfx/articles/memory.md rather than summarising it, which tells you it needs its own explanation. The using declarations in the training loop are not decoration: tensors hold native memory, and a long-running service that skips disposal is a leak you will find in profiling rather than in review.
Editorial conclusion
Adopt TorchSharp when you need to train or run LibTorch models inside a .NET service and cannot host a Python process. Do not adopt it expecting code you can copy from Python unchanged, because named arguments and module construction both differ. Before starting, pick the backend package that matches your OS and CPU architecture, and check whether your Macs are Apple Silicon, since Intel support ended at 0.102.8.
Frequently asked questions
Which TorchSharp NuGet package should I install?
Use one of the bundled packages so the managed library and a matching native backend arrive together: TorchSharp-cpu for Linux, Windows and OSX, or TorchSharp-cuda-windows and TorchSharp-cuda-linux for CPU/CUDA 12.1. Otherwise compose your own from the libtorch packages.
Does TorchSharp work on Intel Macs?
No. Intel hardware support was deprecated in libtorch 2.4.0, and the README states 0.102.8 is the last TorchSharp version that works on Intel Macintosh hardware. From 0.103.0 only Apple Silicon Macs are supported.
Why is there no constructor for TorchSharp modules like Conv1d?
A class and a method cannot share a name in a scope, so the class declarations moved into a nested Modules scope and each got a factory method with the original name. Module conv1 = Conv1d(...) creates an instance of Modules.Conv1d, which derives from torch.Module.
Can I copy Python PyTorch code into C# with TorchSharp?
Not unchanged. C# uses a colon for named arguments where Python uses an equals sign, and PyTorch functions take enough parameters that named arguments are common. TorchSharp also uses .NET enumeration types where PyTorch passes strings.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dotnet-torchsharp)Was this useful?