unity-csharp-fundamentals

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unity C# Fundamentals - Essential Coding Patterns

Unity C# 基础核心 - 必备编码模式

Overview

概述

Core Unity C# patterns for safe, maintainable code. Not optimizations but fundamental practices.
Foundation Required: C# basics, Unity MonoBehaviour lifecycle
Core Topics: TryGetComponent, SerializeField, RequireComponent, Null-safe patterns, Lifecycle management
核心Unity C#模式,用于编写安全、可维护的代码。这些不是优化技巧,而是基础实践
前置要求:C#基础知识、Unity MonoBehaviour生命周期
核心主题:TryGetComponent、SerializeField、RequireComponent、空安全模式、生命周期管理

Essential Patterns

必备模式

TryGetComponent (Required)

TryGetComponent(必用)

Always use
TryGetComponent
instead of
GetComponent
:
csharp
// ❌ WRONG
Rigidbody rb = GetComponent<Rigidbody>();
rb.velocity = Vector3.zero;  // NullReferenceException!

// ✅ CORRECT
Rigidbody rb;
if (TryGetComponent(out rb))
{
    rb.velocity = Vector3.zero;
}

// ✅ Cache in Awake with validation
private Rigidbody mRb;

void Awake()
{
    if (!TryGetComponent(out mRb))
    {
        Debug.LogError($"Missing Rigidbody on {gameObject.name}", this);
    }
}
始终使用
TryGetComponent
而非
GetComponent
csharp
// ❌ WRONG
Rigidbody rb = GetComponent<Rigidbody>();
rb.velocity = Vector3.zero;  // NullReferenceException!

// ✅ CORRECT
Rigidbody rb;
if (TryGetComponent(out rb))
{
    rb.velocity = Vector3.zero;
}

// ✅ Cache in Awake with validation
private Rigidbody mRb;

void Awake()
{
    if (!TryGetComponent(out mRb))
    {
        Debug.LogError($"Missing Rigidbody on {gameObject.name}", this);
    }
}

Global Object Search (Unity 2023.1+)

全局对象搜索(Unity 2023.1+)

csharp
// ❌ OBSOLETE - DON'T USE
GameManager manager = FindObjectOfType<GameManager>();

// ✅ CORRECT - Fastest (unordered)
GameManager manager = FindAnyObjectByType<GameManager>();

// ✅ CORRECT - Ordered
GameManager manager = FindFirstObjectByType<GameManager>();

// ✅ Multiple objects
Enemy[] enemies = FindObjectsByType<Enemy>(FindObjectsSortMode.None);
csharp
// ❌ OBSOLETE - DON'T USE
GameManager manager = FindObjectOfType<GameManager>();

// ✅ CORRECT - Fastest (unordered)
GameManager manager = FindAnyObjectByType<GameManager>();

// ✅ CORRECT - Ordered
GameManager manager = FindFirstObjectByType<GameManager>();

// ✅ Multiple objects
Enemy[] enemies = FindObjectsByType<Enemy>(FindObjectsSortMode.None);

SerializeField Pattern

SerializeField 模式

csharp
// ❌ WRONG: Public field
public float speed;

// ✅ CORRECT: SerializeField + private
[SerializeField] private float mSpeed = 5f;

// ✅ With Inspector helpers
[SerializeField, Tooltip("Units/second"), Range(0f, 100f)]
private float mMoveSpeed = 5f;

public float Speed => mSpeed;  // Read-only access
csharp
// ❌ WRONG: Public field
public float speed;

// ✅ CORRECT: SerializeField + private
[SerializeField] private float mSpeed = 5f;

// ✅ With Inspector helpers
[SerializeField, Tooltip("Units/second"), Range(0f, 100f)]
private float mMoveSpeed = 5f;

public float Speed => mSpeed;  // Read-only access

RequireComponent

RequireComponent

csharp
[RequireComponent(typeof(Rigidbody))]
[DisallowMultipleComponent]
public class PhysicsObject : MonoBehaviour
{
    private Rigidbody mRb;

    void Awake()
    {
        TryGetComponent(out mRb);  // Guaranteed to exist
    }
}
csharp
[RequireComponent(typeof(Rigidbody))]
[DisallowMultipleComponent]
public class PhysicsObject : MonoBehaviour
{
    private Rigidbody mRb;

    void Awake()
    {
        TryGetComponent(out mRb);  // Guaranteed to exist
    }
}

Unity Null Safety

Unity 空安全

csharp
// ❌ WRONG: C# null operators don't work with Unity Objects
Transform target = mCached ?? FindTarget();  // Broken!
mEnemy?.TakeDamage(10);  // May fail after Destroy

// ✅ CORRECT: Explicit null check
Transform target = mCached != null ? mCached : FindTarget();

if (mEnemy != null)
{
    mEnemy.TakeDamage(10);
}
csharp
// ❌ WRONG: C# null operators don't work with Unity Objects
Transform target = mCached ?? FindTarget();  // Broken!
mEnemy?.TakeDamage(10);  // May fail after Destroy

// ✅ CORRECT: Explicit null check
Transform target = mCached != null ? mCached : FindTarget();

if (mEnemy != null)
{
    mEnemy.TakeDamage(10);
}

Lifecycle Order

生命周期顺序

csharp
void Awake()     { /* 1. Self-init, cache components */ }
void OnEnable()  { /* 2. Subscribe events */ }
void Start()     { /* 3. Cross-object init */ }
void OnDisable() { /* 4. Unsubscribe events */ }
void OnDestroy() { /* 5. Final cleanup */ }
csharp
void Awake()     { /* 1. Self-init, cache components */ }
void OnEnable()  { /* 2. Subscribe events */ }
void Start()     { /* 3. Cross-object init */ }
void OnDisable() { /* 4. Unsubscribe events */ }
void OnDestroy() { /* 5. Final cleanup */ }

Unity C# 9.0 Limitations

Unity C# 9.0 限制

Important: Unity's Mono/IL2CPP runtime lacks
IsExternalInit
.
init
accessor causes compile error CS0518.
csharp
// ❌ COMPILE ERROR in Unity
public string Name { get; private init; }

// ✅ Use private set
public string Name { get; private set; }

// ✅ Or readonly field + property
private readonly string mName;
public string Name => mName;
Available: Pattern matching, switch expressions, covariant returns NOT Available:
init
,
required
(C# 11)
重要提示:Unity的Mono/IL2CPP运行时不支持
IsExternalInit
。使用
init
访问器会导致编译错误CS0518。
csharp
// ❌ COMPILE ERROR in Unity
public string Name { get; private init; }

// ✅ Use private set
public string Name { get; private set; }

// ✅ Or readonly field + property
private readonly string mName;
public string Name => mName;
可用特性:模式匹配、switch表达式、协变返回 不可用特性
init
required
(C# 11)

Quick Reference

快速参考

PatternRule
Component accessAlways
TryGetComponent
, never bare
GetComponent
Serialization
[SerializeField] private
, not
public
DependenciesUse
[RequireComponent]
for guaranteed components
Null checksExplicit
!= null
, not
??
or
?.
CachingGet in
Awake
, reuse everywhere
EventsSubscribe in
OnEnable
, unsubscribe in
OnDisable
Global search
FindAnyObjectByType
(fastest), not
FindObjectOfType
模式规则
组件访问始终使用
TryGetComponent
,绝不直接使用
GetComponent
序列化使用
[SerializeField] private
,而非
public
依赖管理使用
[RequireComponent]
确保依赖组件存在
空值检查显式使用
!= null
,而非
??
?.
缓存策略
Awake
中获取组件,全局复用
事件处理
OnEnable
中订阅事件,
OnDisable
中取消订阅
全局搜索使用
FindAnyObjectByType
(最快),而非
FindObjectOfType

Reference Documentation

参考文档

Component Access Patterns

组件访问模式

  • TryGetComponent variations and interface-based access
  • GetComponentInChildren/Parent patterns
  • Allocation-free multiple component access
  • Caching strategies and performance comparisons
  • TryGetComponent的变体及基于接口的访问方式
  • GetComponentInChildren/Parent的使用模式
  • 无分配的多组件访问
  • 缓存策略与性能对比

Attributes and Patterns

特性与模式

  • Complete serialization attribute reference
  • Inspector customization (Header, Tooltip, Range)
  • Execution order control
  • Conditional compilation
  • 完整的序列化特性参考
  • 检视面板自定义(Header、Tooltip、Range)
  • 执行顺序控制
  • 条件编译

Language Limitations

语言限制

  • init
    accessor alternatives with code examples
  • Records in Unity (limitations and workarounds)
  • required
    modifier alternatives
  • Available C# 9.0 features in Unity
  • init
    访问器的替代方案及代码示例
  • Unity中的Records(限制与变通方案)
  • required
    修饰符的替代方案
  • Unity中可用的C# 9.0特性