私は次のことを尋ねられたインタビューを受けました:
質問:名前と署名は同じだが戻り型が異なるメソッド。それは可能ですか、そして彼は私に尋ねたこのタイプと呼ばれるものは何ですか。
誰か私に次のことを教えてください:
どのようなシナリオでも上記のことは可能ですか(少なくともベースクラスにあり、派生クラスに少なくとも1つありますか?)その場合、どのタイプですか?コンパイルまたは実行時のポリモーフィズムが好きですか?
コンパイル時のポリモーフィズムで、シグネチャとともにメソッドの戻り値の型も異なる場合はどうなりますか?ただし、関数の名前のみが同じです。コンパイル時のポリモーフィズムはまだですか?
オーバーライドで、戻り値の型は異なるがメソッド名とシグネチャが同じ場合はどうなりますか?出来ますか ? (彼は私にこの質問をしました、私は間違って答えました:()助けてください。
ありがとうございました
メソッドは、ベース型で宣言されたものより派生した型を返すことができます。
public interface ISomeInterface
{
object GetValue();
}
public class SomeClass : ISomeInterface
{
public string GetValue() { return "Hello world"; }
}
これは、Javaでサポートされていますが、C#ではサポートされていません。上記の戻り型はSomeClass.GetValue
はstring
ではなくobject
です。
Return-typeのみに基づいてメソッドをオーバーロードできないことに注意してください。つまり、以下は無効です。
public class SomeClass
{
public int GetValue() { return 1; }
public string GetValue() { return "abc"; }
}
インターフェイスを使用して同様のことができますが、明確にするために明示的に実装する必要があります。
public interface IValue<T> { T GetValue(); }
public class SomeClass : IValue<int>, IValue<string>
{
string IValue<string>.GetValue() { return "abc"; }
int IValue<int>.GetValue() { return 1; }
}
名前は同じでもパラメーターが異なる場合、これはメソッドのオーバーロードです。これは、ポリモーフィズム(アドホックポリモーフィズム)の一種です。オーバーロードは、コンパイルタイプで静的に解決されます(dynamic
を使用している場合を除き、その場合は実行時に遅延されます)。
パラメーターの数とタイプの両方をオーバーロードできるため、次のすべてが有効です。
public void DoSomething(int value) { }
public void DoSomething(string value) { }
public void DoSomething(int value, string value) { }
これらのメソッドの戻り値の型を変えることができることに注意してください-メソッドは戻り値の型だけに基づいてオーバーロードすることはできませんが、パラメータリストが異なる場合は異なることができます。
繰り返しますが、これは戻り値型の共分散であり、C#ではサポートされていません。
C#では、次のようなメソッドを持つことはできません
int Foo() { return 1; }
void Foo() { return; }
これらは、戻り値のタイプ以上に異なる必要があります。
引数が異なる場合は、準備ができています。
int Foo(string x) { return 1; }
void Foo(double x) { return; }
戻り値型の共分散はC#ではサポートされていませんが、明示的な実装とメソッドの非表示を使用してエミュレートすることができます。これは、ADO.NET APIで徹底的に使用されるパターンです。
例えば。:
_public interface ISomeValue { }
public abstract class SomeValueBase : ISomeValue { }
public class SomeValueImpl : SomeValueBase { }
public interface ISomeObject { ISomeValue GetValue(); }
public abstract class SomeObjectBase : ISomeObject
{
ISomeValue ISomeObject.GetValue() { return GetValue(); }
public SomeValueBase GetValue() { return GetValueImpl(); }
protected abstract SomeValueBase GetValueImpl();
}
public class SomeObjectImpl : SomeObjectBase
{
protected override SomeValueBase GetValueImpl() { return GetValue(); }
public new SomeValueImpl GetValue() { return null; }
}
_
それを行うことで、GetValue()
を呼び出した最終的な結果は、利用可能な最も具体的なタイプと常に一致することです。
はい、以下に示すように、明示的なインターフェイス実装を使用して、同じ署名で異なる戻り値の型を持つ複数のメソッドを使用することができます。
public interface I {
int foo();
}
public class C : I {
double foo() { return 2.0; }
int I.foo() { return 4; }
}
これはインタビューの質問であるため、少し答えて実行できます。
厳密に言えば、異なる戻り型と同じ署名を持つメソッドは使用できません。ただし、大まかに言って、具体的な実行時の戻り値の型が異なるメソッドを実装する方法は多数あります。
1つは汎用パラメーターの使用です。もう1つは、複数の実装を持つインターフェイスまたはスーパークラスを返すことです。または、オブジェクトを返すことができます。オブジェクトは何にでもキャストできます。
多くの人が述べたように、「new」キーワードを使用して、サブクラスの同じメソッドの派生型を返すこともできます。
dynamic
戻り型を使用できます:
Config.Parse(string settingName)型インターフェイスの標準的な並べ替えをすべて使用するさまざまな型の構成ファイルを解析していたため、最近この正確な問題が発生しました。
最初のソリューションの一般的な方法:
_T Parse<T> (string settingName)
{
T Result;
doParsing...
return T;
}
_
someSetting = Parse<float>("param");
などの使用されている型を明示的に指定する必要があるため、私はこれが本当に好きではありませんでした
したがって、私が使用したソリューションは冗長ですが、私の意見でははるかにきれいです:
_T Parse<T> (string settingName, out T result)
{
doParsing...
return T;
}
_
Out変数と戻り値は同一なので、少し冗長になりますが、それにより、かなりきれいなインターフェースだと思います。
_setting = Parse("intparam", out setting);
_
そして、冗長性をわずかに犠牲にして、戻り値の型によってのみ異なるメソッドを取得します。それに加えて、たとえばdoubleからfloatにデータのタイプが変更された場合、すべてが正常に機能し続けますが、最初のソリューションでは暗黙的にエラーを変換することはできません。
はい、同じメソッド、同じパラメーター(カスタマイズが必要)、および異なる戻り値を使用できます。
以下のコードに従ってください、それはあなたを助けるかもしれません。
public class myClass
{
public int myReturn() { return 123; }
public string myReturn(string temp = null) { return "test"; }
}
つまり、関数を実行するためにパラメーターが必要ですが、オプションのパラメーターとしてstring temp = null
があるため、パラメーターを無視することができます。