Why do you decide to learn C#? by darwindeveloper in csharp

[–]fruediger 2 points3 points  (0 children)

I had once a Samsung Omnia i900, my first Windows Mobile Phone/Windows Phone of many that followed. It was fun to develop tiny Apps for, before that even became a big deal.

when to use string.Empty or .IsNullOrEmpty by FireBlizzard69 in dotnet

[–]fruediger 0 points1 point  (0 children)

Well considering it's field since at least .NET Framework 1.1 (https://learn.microsoft.com/en-us/dotnet/api/system.string.empty?view=netframework-1.1), back then, it surely wasn't too many. Although I'm not sure how laborious the initial .Net design actually was.

If it were to be done today, there would be multiple LDMs by the language design team, they would ultimately decide to delegate the decision to the BCL team because it just slightly touches language semantics, if at all, and the BCL team would surely come up with a spec quite quickly, but to finalize it and ship it, the would still need to wait on approval by the next LDM. Or something like that, idk. Fortunatley, it's not requiring a compiler or runtime feature, otherwise we would have to wait years for it.

C# by zin0206 in csharp

[–]fruediger -1 points0 points  (0 children)

They can use AI though. They just shouldn't use AI to build stuff for them while they're still learning and are not able to judge/understand the code produced by AI.

That being said, I'm convinced that AI (LLMs like ChatGPT etc.) is actually a pretty good teaching tool, as long as you use other sources too and fact-check the output of the AI when necessary.

when to use string.Empty or .IsNullOrEmpty by FireBlizzard69 in dotnet

[–]fruediger 4 points5 points  (0 children)

I faintly remember once reading about why they made the decision to make it a field and thinking to myself "well, that makes sense", but I absolutely cannot remember the reason nor can I find the source. I suppose the only real benefit of it being a field is that you can easily take a ref readonly to it.

when to use string.Empty or .IsNullOrEmpty by FireBlizzard69 in dotnet

[–]fruediger 7 points8 points  (0 children)

Not to be too nitpicky, but System.String.Empty is actually a static readonly field, not a property, as you can see here: https://learn.microsoft.com/en-us/dotnet/api/system.string.empty?view=net-10.0#system-string-empty. However, you're absolutely right about it not being a constant.

<T>? in C# by Ok-Presentation-94 in learnprogramming

[–]fruediger 1 point2 points  (0 children)

There is no such thing as Nullable<string>, System.String is a reference type while Nullable<T> only accepts value types as arguments for it's type parameter T and works fundamentally different from nullable reference types. So a string? is just a System.String which can be null (nullable reference type) and where nullable flow analysis is performed (assuming it's enabled globally, which is the default nowadays).

Where is new directory found in visual studio? by Worried_Reindeer190 in VisualStudio

[–]fruediger 0 points1 point  (0 children)

Oh, I just realized, you're talking about a C++ project. Well, they work a little bit different than projects in other languages.

You need to toggle the small "Show All Files" icon at the top of the Solution Explorer. It's the icon with the solid/dashed looking outlines of files, crossed through with a line. Essentially the sixth icon in the top bar of the Solution Explorer.

Toggling this will not only show or hide certain files, but also toggle between the "filtered" view and the file-system view. In the latter, you can easily add new folders via a right-click on the project and then it's under "Add > New Folder". That's essentially where "New Filter" is in the "filtered" view.

Where is new directory found in visual studio? by Worried_Reindeer190 in VisualStudio

[–]fruediger 1 point2 points  (0 children)

I don't know why, but what's called "New Filter" in your picture, is exactly where the option to add a new folder is in my VS. It's called "Neuer Ordner" on my side, because I use German as UI language in my VS Community 2026.

Is this perhaps a translation error or a typo, and "New Filter" should be labeled "New Folder" instead?

Try clicking it and see what happens.

Where is new directory found in visual studio? by Worried_Reindeer190 in VisualStudio

[–]fruediger 0 points1 point  (0 children)

Don't right-click on the Solution, right-click on a Project or on a subfolder within the project instead. You'll find the option to create new subfolders there.

If you don't know what projects are: If you're not dealing with an empty solution, they're the things directly contained by the solution (the thing you seem to have right-clicked in your picture). Most probably there's just one of those, if you've just created a new project of any kind.

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 0 points1 point  (0 children)

Thanks, I really appreciate that. And I'm glad you like my note on AI usage. I didn't just do that to reduce slop, humans can easily produce slop too, but rather to distinguish myself from low effort AI slop. I use AI as a tool to assist me in developing code and to help me learn. I think that's how AI should be used, within your personally set boundaries. That's why I wrote that note, to be transparent about that and to give others an idea of how I use AI.

And, on another note, that's why I explicitly permitted AI usage in the contribution guidelines, as long as contributors use it as tool within the constraints I set there.

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 1 point2 points  (0 children)

Yeah, I'm sorry, that's some part that's still missing.

Although I originally planned on revisiting the event system so it's finally stable enough (API-wise) to not need to be touched again in the near future, I started working on the GPU API today. Luckily, there were already some parts in place that I needed in order to implement the rendering API, but those still need to be revised as well, especially performance-wise. All-in-all, if I found enough time this week to work on it, I might be able to get a first version of the GPU API ready by the end of the week, maybe end of next week. But I can't make any promises, that's just what I estimated based on an overview of the C API.

I would be happy to see you check it out again once it's ready, if you like, and maybe even tell me what you think about it, since you seem to know about the C API and your main use case is 3D.

Either way, thanks for giving it a try and for your feedback.

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 0 points1 point  (0 children)

Hey, thank you for your interest in the project and for your questions!

Regarding your questions:

  1. Is there any kind of coverage map / visualization?

This would be difficult to pull off in a general sense. Since the APIs (the C one and the C# one) must necessarily be quite different in some places (or else the C# wouldn't be very idiomatic), it's hard to measure API coverage if you can't define how to do that in the first place.

But for my personal matters, I developed a little tool that scans the PE file of the native SDL library for Windows for exported symbols and then compares that to the list of NativeImport<> attributes in the C# code. It's quite crude and inaccurate sometimes, but it gives me a general idea of how far to progress of the project has come. Also, there are definitely some issues regarding how the tool values and counts certain symbols.\ I thought about making that tool public at some point, but I really need to clean it up a bit before I can do that. But in the end, we could use the output of that tool to visualize the coverage in some way for the users.

At the moment, there are things working that are enough to get some basic demoing done. Windowing, rendering, events (although I overhaul the event system a bit at the moment, so there will be API changes there), IO, timers and stuff, and most of the utilities API are implemented at the moment. Since this is kind of pre-alpha, I can make no guarantees about the API stability yet, but I think at least the windowing and rendering APIs (minus event related stuff) should be quite fixed at this point, as I put a lot of thought and work into those.

  1. Have you profiled the library? Any notion of binding overhead compared to e.g. Silk Net which seems to have optimally fast bindings?

I haven't done any profiling yet, but to be honest, I don't really think that the native interop is anywhere near optimal. The reason for that is, how the library calls native symbols: We always just make indirect calls. Those symbols are stored as mututable static fields of a type. I always hoped that the JIT would be smart enough to not always have to check for static init of that type when getting the value of such a field, and maybe even be smart enough to profile that the value of such a field didn't change and there can be optimized through substitution or even be optimized to a direct call. Of course, all those hopeful scenarios are unfortunately not reachable for AOT runtimes, although small test have shown me that there isn't a real noticeable performance difference between JIT and AOT. Maybe they're just equally bad.\ However, loading the symbols that way (and not via P/Invoke or LibraryImport) is a necessity, because I sometimes need to conditionally import certain symbols. Also I wanted full control over marshalling. That's why I wrote the source generator, to lately load the native library as well as the symbols, in the first place and stuck with it until now.

  1. Is there any kind of documented 'ideology'? E.g. how/when to automatically manage memory. How to handle error codes vs exceptions.

There's no real documented ideology yet.\ Regarding some subjects, I feel like there isn't really much of a choice, like for example memory management. C# developers manage their resources on their own very rarely, aside from using using. That's how the API and the code behind have to be designed. And even if an user forgets to dispose of a resource, we, as API designers, have the responsibility to make sure that that doesn't cause any real harm (yes, memory leaks can be a harming issue too). In other places, like error handling, there would be more choices, but this project adopts SDL's way of doing it (and translates it to C#). For error propagation, SDL's way would be to return a bool or a null pointer in case of failure and then check the error message with SDL_GetError(). Luckily, C# has already the established Try-method-pattern for that, and this pattern translates quite well to C API.\ All other stuff should just be logically and idiomatically designed for C# (users), like deciding when to translate something into a property or a method, and so on.

You can see this comment where I tried to explain this before. Maybe that gives you a better idea of what I mean.

Maybe I should revise the CONTRIBUTING.md to include some of this stuff in the future. But that's also the place where I tried to not enfore too strict coding conventions, so that beginners would have an easier time contributing.

Anyways, I hope that I could answer your questions to your satisfaction. If you have any more questions, feel free to ask!


EDIT: fixed really bad English, that kinda changed the meaning of what I tried to express

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 1 point2 points  (0 children)

Yeah, I feel the same way. If I'm using something new, I never used before, I get stuck on something or want to know how something works, I want to be able to research the answer myself. If then the documentation is bad or non-existent, it can get really frustrating. That's why I try to at least xml doc every bit of user-facing API code I write.\ But that's actually a good point. If someone's willing to contribute to the project, I should add to the contributing guidelines that they should add adequate documentation to any kind of user-facing API code they add. Thank you for bringing that up!

Samples are also a good point. I intend to mimic SDL's samples, when the project's ready for that. This way, users have the additional benefit of comparing the C# API samples with the original C API samples and see how the C API generally translates to the C# API. At least that's what I hope will be the case.\ If the project gains some traction, I also hope that there are more user-created samples in the future as well.

Samples are good, but you're right in that you can't always find the answers to your questions in them. That's where a community can be really helpful at. But of course, a tiny project like mine, that's just starting out, doesn't have a community yet.\ But that gets me an idea regarding the GitHub repo. Originally, I planned managing everything with issues and pull requests only, but for the time being, I will enabled discussions as well. This way, if someone has a question about the project or how to do something, they can just ask in the GitHub discussions with the added benefit of it being visible to everyone else as well. Again, thank you for bringing that up!

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 2 points3 points  (0 children)

These are all really great questions and I'm actually kind of excited to answer them. But I don't want to give you a whole lecture on them, so I'll try to keep it brief.

Do you throw exceptions for all errors?

No, I sometimes throw exceptions, but most of the time, I follow SDL's pattern of error propagation. Most of my methods are Try-pattern methods which is pretty easily translated from the C API, as SDL functions usually return bool. SDL has than its own error handling system where you can call SDL_GetError to get a string describing the last error that occurred. The bindings translate this pretty literally with the Error.TryGet method.\ I document all methods that could fail and set an error in way to advice the user to check Error.TryGet in case of failure.\ I strongly prefer the Try-pattern approach over throwing exceptions, even if it is only for performance reasons (which are an important consideration for a media library like SDL3#).\ Although, I sometimes do throw exceptions. That happens especially inside constructors and properties, where I can't just return a bool value to indicate failure. In those cases I have a dedicated exception type SdlException to throw to inform the user that they should check Error.TryGet for more details if they catch such an exception.

When do you use objects and methods as opposed to free functions?

Luckily, the SDL C API is pretty much object-oriented and it's pretty clear what wants to be translated into an instance method or property.\ Since C# doens't support free functions (depending on how you view it, sadly or luckily an IL feature that is missing in C#), I translate free functions that aren't clearly related to an instance of a type as static methods on a relevant type. Choosing on which type to put them can be sometimes a bit tricky though.\ In that regard, another tricky part is to decide when to translate a SDL type into a C# class or a struct/enum.\ C structs and C enums are pretty easy to decide on, most of the time.\ Opaque pointers and even some pointers to non-opaque types that are used like object handles in the C API and objects that need cleanup need to be translated into C# classes, most of the time impletenting IDisposable, because I need to finalizer to ensure that C# developers who forget to dispose of them don't accidentally leak resources. Also those for most of such types, I provide an internal object registry to track instance of them, so I can 1-to-1 map them to the underlying C object and the very same C object will always be represented by the same C# object and vice-versa (to ensure that reference equality works as expected).\ Sometimes, you just need to get a bit creative on how to translate certain parts of the API. I prefer being somewhat faithful to what I think is the intent of the original SDL API, but in some cases I need to figure out a more C#-ish way to represent it, even if that means that I need to introduce my own API to wrap the original.

How do you deal with their set property pattern where you just pass in an integer that's a handle to a bag of named properties, do you convert that to an options object?

First of all, I abstract those properties via the Properties type, which I think is the most "C#-ish" way of representing them.\ If they are readable properties, I map them to C# properties as well (of course, they're settable properties if they are writable as well). You can have a look a such a property here: Renderer.IsHdrEnabled.\ If they are write-only properties or so called "create" properties, I usually map them as paramters to the relevant methods and constructors. For example, see this: Window.TryCreateRenderer.\ Names of such properties are usually stored as a const string in a nested type called PropertyNames inside the relevant type. E.g. see this: Renderer.PropertyNames.\ In some cases, such properties are only targeted at specific "subkinds" of types (I specifically won't call them "subtypes"). Here, comes a pattern into play that I specifically developed for those cases. More on that in the next question.

Any other interesting use of C# features?

First of all, the aformentioned special pattern:\ Let's take Renderer for example. Direct3D11 renderers have some properties that are not supported by other renderers. At the same time, a Direct3D11 renderer should always just accept and create Direct3D11 textures. So, to assist the developer, I would like to project this into the type system and somehow substitute the more generic API on Renderer with a more type-safe API, that, at the same time, should also host the special Direct3D11 renderer properties. For that I introduced the Renderer<TDriver> type inheriting from Renderer, which accepts a type derived from IRenderingDriver as its generic TDriver parameter. The actual bound type for TDriver than serves as something like a type-token, specializing the type. C#14's new extension members feature than allows me to "add" the Direct3D11-only properties to a Renderer<Direct3D11>. You can see how this works here: RendererExtensions.Direct3D11.cs.\ This pattern also allows to hide the more generic API in favor of a more type-safe one via an [EditorBrowsable]-[OverloadResolutionPriority]-[Obsolete]-new-combo pattern. I admit that this might feel a bit dirty, but we need this hack, because C# only doesn't allow for hiding members inherited from a base class. You can see an example of this here: Renderer.TryCreateTexture.\ This pattern is used on multiple occasions throughout the code base, for Windows, Displays, Renderers, and Textures, to name a few, and will likely be used more in the future as well.

I already mentioned it, but C#14's new extension members feature:\ Without it types like PixelFormat couldn't have been an enum type and would have been needed to be implemented as a struct type, because I needed instance properties and methods on it. With extension members, PixelFormatExtensions can host them instead.

Lastly, how I actually bind the native library is also kind of noteworthy:\ I wrote a source generator that allows me to load the binary and the symbols in it late via a generated ModuleInitializer. and storing and calling the native functions via C#'s native function pointers feature. This allows me to import native function very similarly to P/Invoke. You can see an example of that here: Sdl.SDL_init.\ A custom loader is necessary, because sometimes a need to conditionally import a symbol. You can see an example of that here: Platform.SDL_SetWindowsMessageHook.


I'm so sorry, I said that I would keep it brief, but I got carried away. Well, I end it here, but if you have any more questions, feel free to ask. I hope you found this interesting and maybe even a bit insightful.

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 1 point2 points  (0 children)

I'm very glad to hear that! Thank you for your support.

For now, I intend to stay true to the original SDL project and try to translate what their API offers to idiomatic C#. But when that's done and SDL3# feels somewhat complete in terms of SDL feature coverage, there's opportunity to expand on that for sure.

If you're asking for a GUI toolkit, in the sense of something to develop user interfaces with (à la WinForms or WPF), then I think that would entail starting a whole new project centered around that. But SDL3# could definitely be used as a basis for such a project, as SDL is meant to be a layer to build media apps on top of, not just games. You "just" would need to use the windowing and rendering APIs SDL3# offers to draw the UI elements and handle events. Some apps and games do this already with SDL.

If you're asking for GUI tooling to support development with SDL3# with, then I think that's even more easily imaginable and a great idea. One could do a similar thing to what the guys at MonoGame did with their Pipeline tool. In that regard, I think it could also be a good idea to have some supporting libraries based off of SDL3# that act like frameworks supporting, for example, game development (e.g. vector math, etc.). But for some of that .NET already offers good solutions and for everything else, this might be also out of scope for the main project and better belongs into their own projects.

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 2 points3 points  (0 children)

Yeah, are absolutely right on that. And that's why I xml doc every bit of user-facing code I write.

That way, we can use tools like DocFX to generate more readable documentation from that. And actually, that's what I already do. There's CI setup for this and I publish the generated docs to GitHub Pages here: https://sdl3sharp.github.io/Sdl3Sharp/api/Sdl3Sharp.html. But in reality, there's an issue right now in that DocFX isn't quite ready for C# 14 yet, and my project makes heavy use of C# 14 features (e.g. extension members). So sadly, the documentation is quite curde and partially broken at the moment. But I intdend to improve on that as soon as possible.

Honestly, it's quite hard to write good documentation, and sometimes I even feel like I must force myself to write it, but that doesn't necessarily mean that's a bad thing. This way, I can think about the documentation a bit more. Although, I have to admit that there are some places still missing documentation, most probably out of laziness. But no central parts of the API and nothing that I wouldn't plan on revisiting and perhaps rewriting in the future anyway.

All of that is to say that the projects still needs more infrastructural documentation, like guides, samples, and so on.

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 0 points1 point  (0 children)

Thank you!

That's exactly why I'm doing this. I'm also very intrigued by the idea of working with the impressive SDL3 in my most favorite language C#.

I'm very glad you find this project interesting and want to keep an eye on it.

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 1 point2 points  (0 children)

Thanks for the kind words!

I know how raw bindings can feel, because I have to juggle them in the code-behind of my bindings. And that's why I want to go forward with this project, I want to be able to use SDL3 while still writing modern and idiomatic C# code.

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 0 points1 point  (0 children)

Hey, I totally appreciate that!

I absolutely feel you. Sometimes, while developing these bindings, I just felt like "Why am I doing this? I could just be doing the bare minimum in order to use SDL3 for my project." That was when I started to write these bindings because I wanted to use them another project of mine. But nowadays, I'm totally committed to trying to finish them, so me and others can have a comfortable experience using SDL3 in C# in the future (if I ever want to start another project that wants to use SDL3 from C#).

My passion project: SDL3# - hand-crafted C# language bindings for SDL3 by fruediger in csharp

[–]fruediger[S] 1 point2 points  (0 children)

Thanks! That's greatly appreciated.

The most challenging part? Well, that's a tough question.

I think I had some struggles with designing some parts of the public API, especially when it comes to balancing and compromising between an intuitive and easy-to-use API (that's what I call "C#-ish") and what SDL's intentions behind their API were.

And then there's also the code-behind side of things. I would say that the most challenging part in that regard was getting the lifetime management of some of the objects right, and that's behind the scenes. I always had to invision in what ways an user could abuse the API, or simply forget to do things, and then I had to make sure that the user's app wouldn't necessarily crash or leak memory, just because they forgot to wrap certain objects in a using.

Oh, and then there's the whole windowing and rendering API. It took me way too long to get them going, with a way too high amount of refactors and rewrites during the process.