這個做法在討論多載這個特性與多載擴充方法是非常不好的。

用下面這段程式碼做例子,它會使用 Format 擴充方法直接將 Person 的名稱進行輸出,注意到擴充方法的 namespace 為 ConsoleExtensions 與主程式的 namespace 不同。

using ConsoleExtensions;

void Main()
{
	List<Person> somePresidents = new List<Person>{
		 new Person{FirstName = "George",LastName = "Washington" },
		 new Person{FirstName = "Thomas",LastName = "Jefferson" },
		 new Person{FirstName = "Abe",LastName = "Lincoln" }
	};
		 
	foreach (Person p in somePresidents)
		Console.WriteLine(p.Format());
}

public sealed class Person
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
}

namespace ConsoleExtensions
{
	public static class ConsoleReport
	{
		public static string Format(this Person target) => $"{target.LastName,20},{target.FirstName,15}";
	}
}

Washington,         George
 Jefferson,         Thomas
   Lincoln,            Abe

從上面的擴充方法可以得知,它是用來組合名稱後回傳一個字串,但是今天有另一個需求想要輸出 XML 格式的話,你可能會在寫一個擴充方法。

namespace XmlExtensions
{
	public static class XmlReport
	{
		public static string Format(this Person target) =>
			new XElement("Person",
				new XElement("LastName", target.LastName),
				new XElement("FirstName", target.FirstName)
				).ToString();
	}
}

最後再把最上方的 namespace 切換成 XmlExtensions 就可以做到不修改主程式也能改變輸出結果。

using XmlExtensions;

void Main()
{
	List<Person> somePresidents = new List<Person>{
		 new Person{FirstName = "George",LastName = "Washington" },
		 new Person{FirstName = "Thomas",LastName = "Jefferson" },
		 new Person{FirstName = "Abe",LastName = "Lincoln" }
	};
		 
	foreach (Person p in somePresidents)
		Console.WriteLine(p.Format());
}


<Person>
  <LastName>Washington</LastName>
  <FirstName>George</FirstName>
</Person>
<Person>
  <LastName>Jefferson</LastName>
  <FirstName>Thomas</FirstName>
</Person>
<Person>
  <LastName>Lincoln</LastName>
  <FirstName>Abe</FirstName>
</Person>

這種寫法就是本做法提到的多載擴充方法,雖然看起來可能但實際上會碰到某些問題,例如忘記把 using 加上去整個程式就沒版法編譯了, 或是同一個 class 的兩個方法需要個別使用 Xml 與 字串輸出,同時引用 namespace 後導致編譯器不知道要選擇哪一個版本。

這是一個誤用擴充方法的案例,另外使用擴充方法應該注重在這些類型本就應該有的功能,所以上面的案例中對 Person 進行 Console 輸出 或 Xml 輸出其實跟 Person 沒有關係,這個功能應該使用者自行處理,關鍵在於擴充方法是用來增強類別原本就應該具有的功能。

所以上面那種行為的功能需求可以改成使用一般的靜態方法即可,並且把名子分開後使用者也比較容易理解方法的內部行為。

public static class PersonReports
{
	public static string FormatAsText(Person target) => $"{target.LastName,20},{target.FirstName,15}";
	public static string FormatAsXML(Person target) => new XElement("Person",
			new XElement("LastName", target.LastName),
			new XElement("FirstName", target.FirstName)
	).ToString();
}

void Main()
{
	List<Person> somePresidents = new List<Person>{
		 new Person{FirstName = "George",LastName = "Washington" },
		 new Person{FirstName = "Thomas",LastName = "Jefferson" },
		 new Person{FirstName = "Abe",LastName = "Lincoln" }
	};
		 
	foreach (Person p in somePresidents)
		Console.WriteLine(PersonReports.FormatAsXML(p));
}

public sealed class Person
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
}

Summary

這個做法透過切換 using 的方式來說明多載擴充方法很容易造成問題,另外提到擴充方法應該是要用來增強類別用的而不是用來操作類別用的。