首页 / C# 入门教程 / this 关键字

C# 入门教程

this 关键字

本教程共 100 篇 · 第 32 篇 · 更新于 2026-07-31 · 约 11 分钟阅读

C#C# 入门教程this面向对象

32. this 关键字

本节目标:学完你能用 this 分清字段和参数,并用它串联构造、传递自身。

this 指的是「当前这个对象自己」。在类的方法里写 this,就相当于指着正在被操作的那个实例说「是我」。它主要干三件事:区分同名、链式构造、传递自身。

用 this 区分字段与参数

最常见的场景:构造函数或方法的参数名,和类的字段名撞了。这时不加 this,编译器分不清谁是谁,默认指向更近的参数。加 this. 明确指向字段,歧义立刻消失。

namespace CSharpDemo;

class Person
{
    public string Name;

    public Person(string name)
    {
        this.Name = name;   // 左边是字段,右边是参数
    }
}
Tip

新手建议:命名时给参数加前缀(如 _name)也能避开冲突。但用 this.字段 是最直白、最不容易看错的写法,团队里也很常见。

如果不写 this 会怎样?看下面的对比,Name = name 把参数赋给了参数自己,字段依然是 null

namespace CSharpDemo;

class Demo
{
    public string Name;
    public Demo(string name)
    {
        Name = name;        // 这里没冲突,参数名和字段不同,正常
    }
}

当参数也叫 name 却漏了 this,就会出现「赋值无效」的隐蔽 bug,调试半天找不到原因。

this() 链式调用构造函数

上一章讲过,this(...) 让一个构造去调用同类的另一个构造。这里再强调一次它的价值:把多套构造的公共逻辑收敛到一处,避免复制粘贴。

namespace CSharpDemo;

class Student
{
    public int Id;
    public string Name;
    public int Score;

    public Student(int id, string name, int score)
    {
        Id = id;
        Name = name;
        Score = score;
    }

    public Student(int id, string name) : this(id, name, 0)
    {
    }

    public Student() : this(0, "未知", 0)
    {
    }
}

var s = new Student(1, "小华");
Console.WriteLine($"{s.Name} 分数 {s.Score}");   // 小华 分数 0
Note

this(...) 必须写在构造的 : 后面,且它的调用会先于本构造体执行。链式要从「最全的那个」往外派生,别形成环。

把 this 作为参数传递

this 代表当前对象,所以能把「自己」传给别的方法。典型用途是注册回调、把对象交给一个工具方法处理,或实现「把自己加进集合」。

namespace CSharpDemo;

class Logger
{
    public static void Print(Person p)
    {
        Console.WriteLine($"记录:{p.Name}");
    }
}

class Person
{
    public string Name;
    public void Report()
    {
        Logger.Print(this);   // 把当前 Person 交给 Logger
    }
}

var p = new Person { Name = "小强" };
p.Report();

这里 Report 里用 this 把自身传进 Logger.Print。读代码时一眼能看出「传的就是我自己」。

this 不能在哪里用

this 只在「实例成员」里有意义。在 static 方法里没有「当前对象」可言,写 this 会直接报错。

namespace CSharpDemo;

class Calc
{
    public static int Add(int a, int b) => a + b;
    // 静态方法里不能用 this,因为它不属于某个实例
}
Tip

记一个口诀:有对象才有 thisstatic 没有对象,所以 static 里没有 this

this 在底层是什么

从内存视角看,对象的数据存放在托管堆上,this 就是指向「当前这块数据」的引用。每个实例方法其实都隐含地多了一个 this 参数——调用 p.Report() 时,系统把 p 作为 this 传进去。所以方法内部才能知道自己在操作哪个对象。

namespace CSharpDemo;

class Counter
{
    public int Value;
    public void Add(int n) => Value += n;   // 等价于操作 this.Value
}

理解这一点,就能明白为什么 static 方法没有 this:它不属于某个对象,自然没有「当前对象」可传。

this 作返回值:写出链式调用

方法里写 return this;,就能把「自己」交回去,从而连续点出多个调用,这种风格叫流式(fluent)API。

namespace CSharpDemo;

class Builder
{
    private List<string> items = [];
    public Builder Add(string s)
    {
        items.Add(s);
        return this;          // 返回当前实例,继续链式
    }
    public void Show() => Console.WriteLine(string.Join(",", items));
}

new Builder().Add("笔").Add("本").Add("尺").Show();
Tip

返回 this 时要确认方法确实在「配置当前对象」,别在会新建对象的方法里误用,否则链式会串错对象。

多层构造的 this 链

this(...) 可以层层往下派生。下面三级构造都最终归到最全的那个,避免重复。

namespace CSharpDemo;

class Order
{
    public int Id;
    public string Address;
    public decimal Price;

    public Order(int id, string address, decimal price)
    {
        Id = id; Address = address; Price = price;
    }
    public Order(int id, string address) : this(id, address, 9.99m) { }
    public Order(int id) : this(id, "未知地址") { }
}

Console.WriteLine(new Order(1).Price);    // 9.99

命名约定:避免同名冲突的另一种思路

除了 this.字段,有人喜欢给私有字段加前缀(如 _name)或后缀,让参数直接写 name 也不冲突。两种风格都常见,关键是团队统一。

Note

常见误区:在 static 方法里写 this 会立即报错,因为静态成员没有「当前对象」。记住「有对象才有 this」就不会错。

在 lambda 里捕获 this

在实例方法内部的 lambda 表达式中,可以直接使用 this 访问对象成员,因为 lambda「捕获」了当前实例。下面的计数器每秒(示意)回调时仍能改到 total

namespace CSharpDemo;

class Clock
{
    private int total;
    public void Start()
    {
        Action tick = () => { total++; Console.WriteLine(total); };
        tick();
        tick();
    }
}

new Clock().Start();   // 1 然后 2
Note

lambda 捕获 this 会间接延长对象生命周期,因为委托持有了对实例的引用。短生命周期对象被长生命周期委托捕获,可能导致内存暂不清。

什么时候其实不必写 this

当参数和字段不同名时,写不写 this 结果一样。此时硬加 this. 反而显得啰嗦。约定是:只有「能消除歧义」或「强调这是实例成员」时才写。

namespace CSharpDemo;

class Person
{
    public string Name;
    public void Set(string value) => Name = value;  // 没冲突,可省 this
}
Tip

新手另一个误区:在 static 方法里写 this 会立即报错,因为静态成员没有「当前对象」。记住「有对象才有 this」就不会错。

this 在结构里同样成立

this 不只属于类,值类型 struct 的实例方法里也有 this,指向当前那个值。其行为一致:区分字段与参数、传递自身都照常工作。

namespace CSharpDemo;

struct Point
{
    public int X;
    public int Y;
    public Point(int x, int y)
    {
        this.X = x;     // 结构里同样用 this 区分
        this.Y = y;
    }
}

var p = new Point(2, 3);
Console.WriteLine($"({p.X},{p.Y})");   // (2,3)

静态类没有 this

不仅是静态方法,整个静态类都没有实例,自然不存在 this。在静态类里任何地方写 this 都会报错。这与「实例成员才有 this」是同一句话的两面。

Note

一句话记牢:只要写 this,就一定处在「某个具体对象」的上下文里;static 无论方法还是类,都脱离具体对象,所以都没有 this

一个易错点:this 不能进静态初始化

静态字段的初始化器里不能用 this,因为静态字段属于类、早于任何对象存在,此时「当前实例」根本还没出生。

namespace CSharpDemo;

class Demo
{
    // private static int x = this.Y;   // 错误:静态上下文无 this
    public int Y;
}
Note

这条规则和「静态方法不能有 this」是同一根源:凡是 static 的领域,都不存在具体对象,自然没有 this 可用。

this 为什么这样设计

this 不是凭空多出来的。C# 的实例方法在底层都隐含携带一个「当前对象」引用——调用 p.Report() 时,运行时把 p 一并传进去,方法体内才认得 this 是谁。把这件事显式暴露成 this 关键字,好处是:方法内部既能访问自己的字段,又能在「字段和参数同名」时精准指定目标。换句话说,this 是「实例方法天然拥有当前对象」这一事实的语法出口。

Note

正因为 this 代表「当前对象」,它不能被赋值为别的实例(写 this = other; 非法),也不能当 ref/out 参数传出去——它扮演的是只读的「我是谁」。

this 与下划线前缀:两种风格怎么选

区分「字段 vs 参数」,业界有两类常见写法:

  • this.字段:直观,谁都能一眼看出指向实例成员;
  • 给私有字段加 _ 前缀(如 private string _name;),参数直接写 name,天然不冲突。

两种都对,关键在团队统一。本书示例偏向 this.字段,因为零基础读者最不容易看错。C# 12 还引入了主构造器(primary constructor),参数直接成为字段,连 this 都不用写:

namespace CSharpDemo;

class Person(string name)          // name 已是字段,无需 this 区分
{
    public void Show() => Console.WriteLine(name);
}
Tip

用主构造器时,参数名就是字段名,根本不会出现同名冲突,也就不需要 this 来救场。这是现代 C# 推荐的写法。

常见误区与最佳实践

  1. 只在必要处写 this:参数与字段不同名时,加 this. 纯属噪音;有歧义或想强调「这是实例成员」才写。
  2. static 里绝不用 this:静态方法/静态类没有「当前对象」,写 this 立即报错。
  3. return this 要谨慎:它适合「配置当前对象」的流式 API,别在会新建对象的方法里误用,否则链式会串错对象。
  4. 别在 property 的 get 里用 this 做重活:读取属性本应轻快,若 this 触发大量计算,调用方会误以为很便宜。

小结

this 指「当前实例」,底层是指向对象数据的引用,实例方法(包括 struct 的实例方法)都隐含带着它。写 this.字段 能在参数同名时精准命中字段;this(...) 把多个构造函数串起来去重;把 this 当参数能方便地把自身交给别的方法;return this 还能写出链式调用;lambda 也会捕获 this 来访问实例成员。而在 static 上下文里(无论静态方法还是静态类,包括静态字段初始化器),因为它不挂靠任何对象,不能使用 this。当参数与字段不同名时,写 this 并非必须。