This is a very good question, as I have seen a number of C++ developers write terrible C# code.
It is best not to think of C# as "C++ with nicer syntax". They are different languages which require different approaches in your thinking. C++ forces you to be constantly thinking about what the CPU and memory will be doing. C# is not like that. C# specifically is designed so that you are not thinking about the CPU and memory and instead are thinking about the business domain you are writing for.
One example of this that I have seen is that a lot of C++ developers will like to use for loops because they're faster than foreach. This is usually a bad idea in C# because it restricts the possible types of the collection being iterated over (and therefore the re-usability and flexibility of the code).
I think that the best way to readjust from C++ to C# is to try and approach coding from a different perspective. At first this will be hard, because over the years you will be completely used to using the "what are the CPU and memory doing" thread in your brain to filter the code you write. But in C#, you should instead be thinking about the relationships between the objects in the business domain. "What do I want to do" as opposed to "what is the computer doing".
If you want to do something to everything in a list of objects, instead of writing a for loop on the list, create a method which takes an IEnumerable<MyObject>
and use a foreach
loop.
Your specific examples:
Controlling the lifetime of resources that require deterministic cleanup (like files). This is easy having using in hand but how to use it properly when ownership of the resource is being transfered [... betwen threads]? In C++ I would simply use shared pointers and let it take care of 'garbage collection' just at the right time.
You should never do this in C# (particularly between threads). If you need to do something to a file, do it in one place at one time. It's ok (and indeed good practice) to write a wrapper class which manages the unmanaged resource which gets passed between your classes, but don't try and pass a File between threads and have separate classes write/read/close/open it. Don't share ownership of unmanaged resources. Use the Dispose
pattern to deal with cleanup.
Constant struggling with overriding functions for specific generics (I love things like partial template specialization in C++). Should I just abandon any attempts to do any generic programming in C#? Maybe generics are limited on purpose and it is not C#-ish to use them except for a specific domain of problems?
Generics in C# are designed to be generic. Specializations of generics should be handled by derived classes. Why should a List<T>
behave differently if it's a List<int>
or a List<string>
? All of the operations on List<T>
are generic so as to apply to any List<T>
. If you want to change the behaviour of the .Add
method on a List<string>
, then create a derived class MySpecializedStringCollection : List<string>
or a composed class MySpecializedStringCollection : IList<string>
which uses the generic internally but does things in the different way. This helps you avoid violating the Liskov Substitution Principle and royally screwing others who use your class.
Macro-like functionality. While generally a bad idea, for some domain of problems there is no other workaround (e.g. conditional evaluation of a statement, like with logs that should only go to Debug releases). Not having them means that I need to put more if (condition) {...} boilerplate and it is still not equal in terms of triggering side effects.
As others have said, you can use preprocessor commands to do this. Attributes are even better. In general attributes are the best way to handle things which are not "core" functionality for a class.
In short, when writing C#, keep in mind that you should be thinking entirely about the business domain and not about the CPU and memory. You can optimize later if necessary, but your code should reflect the relationships between the business principles you're trying to map.
C#
to generate other code. You can read aCSV
or anXML
or what have you file as an input and generate aC#
or anSQL
file. This can be more powerful than using functional macros.