I give some c# tips and tricks for beginners to improve their C# code if you are familiar with C# then you can directly jump to the tips section.
What is c#?
C# (pronounced “C-sharp”) is a programming language developed by Microsoft as part of the .NET framework. It is a modern, object-oriented language that is designed to be simple, powerful, and easy to use. C# is often used to build Windows desktop applications, but it can also be used to build web applications, mobile apps, and games.
One of the key features of C# is its support for object-oriented programming (OOP). This means that C# code is organized into classes, which are used to represent real-world objects. Each class has methods (functions) and properties (data) that define its behavior and characteristics.
C# is a statically-typed language, which means that variables must be declared with a specific data type before they can be used. C# supports a wide range of data types, including basic types like integers and strings, as well as more complex types like arrays and objects.
C# is a popular language for building a variety of applications, including Windows apps, web applications, mobile apps, and games. It is widely used in the software industry, and there is a large community of developers who use and contribute to C# open-source projects.
C# code best practices [C# tips and tricks for beginners]
- Use clear and descriptive names for variables, methods, and classes.
// Bad:
int x;
int y;
void Update() {
x++;
y++;
}
// Good:
int playerHealth;
int enemyHealth;
void Update() {
playerHealth++;
enemyHealth++;
}
2. Use the const
keyword for values that are not going to change.
// Bad:
int maxHealth = 100;
// Good:
const int MAX_HEALTH = 100;
3. Avoid using magic numbers and define constants for values with a specific meaning.
// Bad:
void Update() {
if (transform.position.y < -10) {
Destroy(gameObject);
}
}
// Good:
const float DESPAWN_THRESHOLD = -10;
void Update() {
if (transform.position.y < DESPAWN_THRESHOLD) {
Destroy(gameObject);
}
}
4. Use comments to explain the purpose of your code.
// Calculates the damage dealt to an enemy based on the player's attack power.
int CalculateDamage(int attackPower) {
return attackPower * 2;
}
5. Consider using design patterns to structure your code in a more organized and reusable way.
// Singleton pattern:
public class GameManager : MonoBehaviour {
public static GameManager instance;
private void Awake() {
if (instance == null) {
instance = this;
DontDestroyOnLoad(gameObject);
} else {
Destroy(gameObject);
}
}
}
// Observer pattern:
public class HealthBar : MonoBehaviour {
private void Start() {
PlayerController.instance.OnHealthChanged += UpdateHealthBar;
}
private void UpdateHealthBar(int newHealth) {
// Update the health bar display based on the new health value.
}
}
6. Follow best practices for writing clean and efficient code.
// Bad:
void Update() {
for (int i = 0; i < transform.childCount; i++) {
Transform child = transform.GetChild(i);
if (child.tag == "Enemy") {
if (child.position.y < -10) {
Destroy(child.gameObject);
}
}
}
}
// Good:
void Update() {
for (int i = 0; i < transform.childCount; i++) {
Transform child = transform.GetChild(i);
if (child.tag != "Enemy") {
continue;
}
if (child.position.y < -10) {
Destroy(child.gameObject);
}
}
}
7. Use code profiling tools to identify and optimize bottlenecks in your code.
using UnityEngine.Profiling;
void Update() {
Profiler.BeginSample("Enemy Despawn");
for (int i = 0; i < transform.childCount; i++) {
Transform child = transform.GetChild(i);
if (child.tag != "Enemy") {
continue;
}
if (child.position.y < -10) {
Destroy(child.gameObject);
}
}
Profiler.EndSample();
}
8. Use the using
statement to manage resources, such as file streams or database connections, to ensure that they are properly disposed of when they are no longer needed.
// Bad:
FileStream stream = new FileStream("myFile.txt", FileMode.Open);
// Use the stream.
stream.Close();
// Good:
using (FileStream stream = new FileStream("myFile.txt", FileMode.Open)) {
// Use the stream.
}
9. Use the string.Format
method to create strings with placeholders for dynamic values. This can make your code more readable and maintainable, especially when building strings with multiple variables.
// Bad:
string message = "The player has " + playerHealth + " health and the enemy has " + enemyHealth + " health.";
// Good:
string message = string.Format("The player has {0} health and the enemy has {1} health.", playerHealth, enemyHealth);
10. Use the string.Join
method to concatenate a list of strings into a single string, separated by a specified delimiter. This can be more efficient than using a loop to manually build the string.
// Bad:
string items = "";
foreach (string item in inventory) {
items += item + ", ";
}
items = items.TrimEnd(',', ' ');
// Good:
string items = string.Join(", ", inventory);
11. Use the [SerializeField]
attribute to expose private fields in the Unity inspector, allowing you to set their values in the editor without making them public.
public class PlayerController : MonoBehaviour {
[SerializeField] private float moveSpeed = 5;
private void Update() {
float horizontalInput = Input.GetAxis("Horizontal");
transform.position += Vector3.right * horizontalInput * moveSpeed * Time.deltaTime;
}
}
12. Use the [Range]
attribute to specify a range of valid values for a numeric field in the Unity inspector. This can make it easier to fine-tune certain values in your code.
public class PlayerController : MonoBehaviour {
[Range(1, 10)] [SerializeField] private float moveSpeed = 5;
private void Update() {
float horizontalInput = Input.GetAxis("Horizontal");
transform.position += Vector3.right * horizontalInput * moveSpeed * Time.deltaTime;
}
}
13. Use the Mathf
class to perform common math operations, such as clamping values to a given range or calculating the distance between two points.
float ClampValue(float value, float min, float max) {
return Mathf.Clamp(value, min, max);
}
float DistanceBetweenPoints(Vector3 point1, Vector3 point2) {
return Mathf.Abs(Vector3.Distance(point1, point2));
}
14. Use the Linq
namespace to perform complex queries on collections of data. This can make your code more concise and easier to read, especially when working with large data sets.
using System.Linq;
int GetHighestScore(List<int> scores) {
return scores.Max();
}
List<string> GetNamesStartingWith(List<string> names, char c) {
return names.Where(name => name[0] == c).ToList();
}
15. Use the null-coalescing operator
(??
) to provide a default value for nullable types. This can help to avoid null reference exceptions and make your code more readable. [Check out this c# code tips]
// Bad:
int? value = GetValue();
int result;
if (value == null) {
result = 0;
} else {
result = value.Value;
}
// Good:
int? value = GetValue();
int result = value ?? 0;
16. Use the ternary operator
(? :
) to concisely express conditional logic in a single line of code.
// Bad:
int result;
if (value == 0) {
result = 0;
} else {
result = 1;
}
// Good:
int result = (value == 0) ? 0 : 1;
17. Use the is
operator to check if an object is of a specific type. This can be useful for implementing polymorphism or for handling different types of objects in a single method.
void HandleObject(object obj) {
if (obj is string) {
Debug.Log("Received string: " + obj);
} else if (obj is int) {
Debug.Log("Received int: " + obj);
} else {
Debug.Log("Received unknown object");
}
}
18. Use the as
operator to safely cast an object to a specific type. This will return null
if the object is not of the specified type, rather than throwing an exception.
void HandleObject(object obj) {
string str = obj as string;
if (str != null) {
Debug.Log("Received string: " + str);
} else {
Debug.Log("Object is not a string");
}
}
19. Use the switch
statement to express complex conditional logic in a more readable and maintainable way.
void HandleInput(string input) {
switch (input) {
case "left":
Debug.Log("Move left");
break;
case "right":
Debug.Log("Move right");
break;
case "jump":
Debug.Log("Jump");
break;
default:
Debug.Log("Invalid input");
break;
}
}
20. Use the @
symbol to create verbatim strings, which allow you to include special characters or line breaks in your string literals without the need to escape them.
string path = @"C:\Users\GameBuddy\Documents
21. Use the UnityEvent
class to create custom events that can be triggered in your code and subscribed to by other components. This can make your code more modular and easier to maintain.
using UnityEngine.Events;
public class Health : MonoBehaviour {
public UnityEvent OnDeath;
public void TakeDamage(int damage) {
// Reduce health.
if (health <= 0) {
OnDeath.Invoke();
}
}
}
public class GameOver : MonoBehaviour {
private void Start() {
Health.instance.OnDeath.AddListener(GameOver);
}
private void GameOver() {
// Show game over screen.
}
}
22. Use the delegate
keyword to create custom delegate types, which allow you to pass methods as arguments to other methods. This can be useful for implementing custom behavior or for creating more flexible code.
delegate void MyDelegate(int value);
void InvokeDelegate(MyDelegate del) {
del(5);
}
void MyMethod(int value) {
Debug.Log("Received value: " + value);
}
void Start() {
InvokeDelegate(MyMethod);
}
23. Use the Task
and async/await
keywords to perform asynchronous operations, such as loading data from a server or saving data to a file. This can help to improve the performance and responsiveness of your code.
using System.Threading.Tasks;
async void LoadData() {
string data = await LoadDataFromServer();
Debug.Log("Received data: " + data);
}
Task<string> LoadDataFromServer() {
return Task.FromResult("Sample data");
}
So, I hope you learn some new from these tips. keep learning, keep growing. And make good quality games and earn good revenue from your game.