System.Stringはクラスであるため参照型になりますが、通常の参照型とは見かけ上の挙動が異なります。

s2s1を代入した時点で両者は同じインスタンスを参照することになり、その後s1"bar"を代入すれば同じ参照となっているs2"bar"となる、という挙動を予想することもできますが、実際にはs2"foo"のままとなります。

このような結果となるのは、s1"bar"を代入している箇所で新しい文字列型のインスタンス"bar"が生成され、それがs1に代入されているためです。 上記のコードが次のようなコードで動作しているととらえるとその挙動が理解しやすいかもしれません(擬似コードのため実際には動作しません)。

参照型としての文字列型の挙動を理解するための疑似コード
using System;

class Sample {
  static void Main()
  {
    string s1 = new string("foo");
    string s2 = s1;

    Console.WriteLine("{0} {1}", s1, s2);

    s1 = new string("bar");

    Console.WriteLine("{0} {1}", s1, s2);
  }
}
実行結果
foo foo
bar foo