C#のメソッド名の制限事項
先日、WPFをまねてTransformクラスとその継承クラスRotateTransform、ScaleTransform、SkewTransform、MatrixTransform、TransformGroupクラスをC#のWindows Formsアプリケーションのプロジェクトで作ろうとしたときのこと。Transformクラスを抽象クラスとして、この階層でTransformメソッドとValueプロパティを定義しようとしました。Transformメソッドはクラス名と同名のため、
error CS0542:'Transform': メンバ名をそれを囲む型の名前と同じにすることはできません
というエラーになりました。
クラス名と同名のメンバを定義できないのです。
C#ではコンストラクタはクラス名と同名を持つというルールがあるので、このような制約があるのかなという予想をしましたが、コンストラクタは通常のメソッドと違って戻り値の指定がない形式のため、構文上明確に区別できます。そう考えると、この制約は言語仕様上で回避できないものではないように思えます。メソッド名がクラス名と同名になることを積極的に禁止する理由がわかりません。
このときは仕方ないので、Transformクラス及び〜Transformクラスは命名を変えて、Transformerクラスと〜Transformerクラスにしました。ただでさえ長い名前がより長くなってしまい不満です。ちなみに、WPFのクラスライブラリではTransformクラスの基底クラスとしてGeneralTransformクラスがあり、Transformメソッドはそこで定義されているため、仮にC#で実装されているとしても問題は発生しません。
確かにクラス名と同名のメソッドやプロパティが定義できるのは通常は混乱のもとだとは思いますが、これを禁止してしまうと、例えば、定義しようとしているクラス名が、継承元クラスのメンバ名や実装するインタフェースのメンバ名とたまたまかぶった場合に不都合が起きます。継承元クラスでオーバーライドしたいメソッドが自分のクラス名と同名の場合、オーバーライドができません。実装したいインターフェースに自分のクラス名と同名のメンバがあった場合には通常の実装はできず、明示的な実装しかできません。ちなみにVBではクラス名と同名のメソッドも定義できます。C#では残念ながら定義できないようですから、命名には注意が必要です。
これに対する反論を想定すると、そもそもクラス名とメソッド名に同じ名称を使う必要があるのか?ということになるかと思いますが、Transform(変換)クラスのTrasform(変換する)メソッドのように動詞と名詞が同じ単語の場合にそのような命名にしたい場合がありえます。私の場合、回避策としてTransformerと命名しましたが、TransformとTransformerは少々ニュアンスが違うので、なんとなく嫌だなと思いました。
最後に検証コードを載せておきます。
VB - VBではクラス名と同名のメソッドを定義できる
Imports System Public Class Transform Public Sub New() End Sub Public Sub Transform() Console.WriteLine("Transform") End Sub End Class Class Program Public Shared Sub Main Dim t As Transform = New Transform() t.Transform() End Sub End Class
using System; public class Transform { public void Transform() { Console.WriteLine("Transform"); } } class Program { public static void Main() { Transform t = new Transform(); t.Transform(); } }
C# - インタフェースのメソッド名と自身のクラス名がかぶった場合は明示的実装のみ可
using System; public interface ITransform { void Transform(); } public class Transform : ITransform { void ITransform.Transform() { Console.WriteLine("Transform"); } } class Program { public static void Main() { ITransform t = new Transform(); t.Transform(); } }