そしてどんな援助にも感謝します。
メソッドから未知のGeneric.List型を返すにはどうすればよいですか。
public void Main()
{
List<A> a= GetData("A");
}
public List<T> GetData(string listType)
{
if(listType == "A")
{
List<A> a= new List<A>()
...
return a;
}
else
{
List<B> b = new List<B>()
return b;
}
}
次の例では、次のようなエラーが表示されます:変換できませんList<A>
からList<T>
これは可能ですか?エラーは「戻り値a」で発生します。コードの行。
また、ラインでエラーが発生しないようにするために何をする必要がありますか:
List<A> a= GetData("A");
おかげで、スティーブン
List<T>
の代わりにIList
を使用してください。
オブジェクトのリストを返すことに限定される代わりに、AとBが共通の基本型から確実に派生するか、共通のインターフェースを実装してから、その基本型またはインターフェースのリストを返すことになります。その効果にジェネリックメソッドに制約を含めます。
List<ICommon> GetData<T>() where T: ICommon
{
}
このようなList<T>
を直接返すことはできません。
どうして?基本的には、List<A>
とList<B>
(またはList<string>
とList<int>
は同じものです)は、完全に独立した2つの独立したクラスと見なされるためです。string
を返すように宣言されている関数からint
を返すことができないのと同様に、intのリストを返すように宣言されている関数から文字列のリストを返すことはできません。ここでの<T>
は、ちょっとした赤いニシンです。文字列と整数の両方を返すジェネリックメソッドを作成できませんでした...
この種の詳細については、 こちら を参照してください。
したがって、あなたがしなければならないことは、両方の型から派生したものを返すことです(それらは「共通」しています)。
John Raschが言うように 、IList
を返すことができます(NONジェネリックに注意してください。つまり、object
sの単なるリストです)。 object
として。残念ながら、リストのタイプを保存する方法はありません。
事前に実際の型を指定できない特別な理由がない限り、メソッド自体をジェネリックにすることができます。
public void Main() {
List<A> a = GetData<A>();
}
public List<TType> GetData<TType>() {
List<TType> list= new List<TType>();
...
return list;
}
以下のオリオンの回答ごとに編集し、AnthonyWJonesが提案した制約を追加
あなたはおそらくAとBが継承しているインターフェイス/抽象クラスを持っているはずです
public interface IMyInterface { }
public class A : IMyInterface { }
public class B : IMyInterface { }
public List<IMyInterface> GetData<T>() where T : IMyInterface
{
List<IMyInterface> myList = new List<IMyInterface>();
if (typeof(T) == typeof(A))
{
myList.Add(new A());
}
if (typeof(T) == typeof(B))
{
myList.Add(new B());
}
return myList;
}
私は最近、同様の問題を解決しなければならず、提案された解決策のどれも満足のいくものではありませんでした。タイプパラメータの制約は実用的ではありませんでした。代わりに、私はメソッドのコンシューマにデータを変更する方法を決定させます。たとえば、部分文字列をTに変換する方法を指示する限り、厳密に型指定されたリストを返すString.Split()の汎用バージョンを作成できます。
責任をコールスタックに移すことを承諾した後(そしてラムダを快適に渡せるようになったら)、このパターンを任意に一般化できます。たとえば、GetData()の方法が変化する場合(一部の応答が明らかに想定しているように)、その関数を呼び出し元のスコープに引き上げることもできます。
デモ:
static void Main(string[] args)
{
var parseMe = "Hello world! 1, 2, 3, DEADBEEF";
// Don't need to write a fully generic Process() method just to parse strings -- you could
// combine the Split & Convert into one method and eliminate 2/3 of the type parameters
List<string> sentences = parseMe.Split('!', str => str);
List<int> numbers = sentences[1].Split(',', str => Int32.Parse(str, NumberStyles.AllowHexSpecifier | NumberStyles.AllowLeadingWhite));
// Something a little more interesting
var lettersPerSentence = Process(sentences,
sList => from s in sList select s.ToCharArray(),
chars => chars.Count(c => Char.IsLetter(c)));
}
static List<T> Split<T>(this string str, char separator, Func<string, T> Convert)
{
return Process(str, s => s.Split(separator), Convert).ToList();
}
static IEnumerable<TOutput> Process<TInput, TData, TOutput>(TInput input, Func<TInput, IEnumerable<TData>> GetData, Func<TData, TOutput> Convert)
{
return from datum in GetData(input)
select Convert(datum);
}
関数型プログラミングの第一人者は、おそらくこの探査で「あっという間にMapを作成するだけです」とあくびをするでしょう。 C++の開発者でさえ、テンプレートテクニック(つまり、STL transform()+ファンクタ)がジェネリックよりも少ない作業で済む例であると主張するかもしれません。しかし、主にC#を実行する人として、型の安全性と慣用的な言語の使用の両方を維持するソリューションを見つけるのは良かったです。
あなたは次のようなことをすることができます:
public void Main()
{
List<int> a = GetData<int>();
List<string> b = GetData<string>();
}
public List<T> GetData<T>()
{
var type = typeof(T);
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = type.GenericTypeArguments[0];
}
if (type == typeof(int))
{
var a = new List<int> { 1, 2, 3 };
return a.Select(v => v != null ? (T)Convert.ChangeType(v, type) : default(T)).ToList();
}
else if (type == typeof(string))
{
var b = new List<string> { "a", "b", "c" };
return b.Select(v => v != null ? (T)Convert.ChangeType(v, type) : default(T)).ToList();
}
}
多分あなたはあなたのニーズにそれを変更することができます。
実行時まで必要なタイプがわからない場合、ジェネリックはおそらくジョブに適したツールではありません。
関数が引数に基づいて動作を大幅に変更する場合(戻り値の型を変更する場合など)は、おそらく2つの関数でなければなりません。
この関数はジェネリックではなく、実際には2つの関数である必要があります。
public void Main() {
List<A> a = GetDataA();
}
public List<A> GetDataA() {
List<A> a= new List<A>()
...
return a;
}
public List<B> GetDataB() {
List<B> b= new List<B>()
...
return b;
}
解決策は、 Visitor Pattern でclientとして機能するコンテナーにデータをカプセル化することです。
最初に、パターンに一致するいくつかのインターフェース:
_/// <summary>
/// The Client
/// </summary>
interface IDataContainer
{
void AcceptDataProcessor(IDataProcessor dataProcessor);
}
/// <summary>
/// The Visitor.
/// </summary>
interface IDataProcessor
{
void WorkOn<TData>(List<TData> data);
}
_
次に、それぞれの実装:
_class DataContainer<TData> : IDataContainer
{
readonly List<TData> list;
public DataContainer(List<TData> list)
{
this.list = list;
}
public void AcceptDataProcessor(IDataProcessor dataProcessor)
{
dataProcessor.WorkOn(list); // Here the type is known.
}
}
class PrintDataProcessor : IDataProcessor
{
public void WorkOn<TData>(List<TData> data)
{
// print typed data.
}
}
_
次に、それを使用します:
_public void Main()
{
var aContainer = GetData("A");
var bContainer = GetData("B");
var printProccessor = new PrintDataProcessor();
aContainer.AcceptDataProcessor(printProccessor); // Will print A data
bContainer.AcceptDataProcessor(printProccessor); // Will print B data
}
public IDataContainer GetData(string listType)
{
if (listType == "A")
return new DataContainer<A>(new List<A>());
if (listType == "B")
return new DataContainer<B>(new List<B>());
throw new InvalidOperationException();
}
_
考えは、DataContainer
は基礎となる型を知っていますが、それを公開しないということです。
GetData
はあらゆる種類のデータを含むことができます(ただし、非表示になっています)。DataContainer
基礎となる型がわかっているため、ワーカーの適切な型付きメソッドを呼び出す必要があります:dataProcessor.WorkOn(list);
これは強力なパターンですが、コードの面で多くのコストがかかります。
私はその方法が遅すぎることを知っていますが、同じ問題を抱えてここに来ました。これが、インターフェイスを使用して解決する方法です。他の人のために投稿するつもりです
public interface IEntity
{
int ID
{
get;
set;
}
}
public class Entity2:IEntity
{
public string Property2;
public int ID
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
}
Entity1についても同様です。
今私のクラス(私のビジネスレイヤー)でこのメソッドがあります
public List<IEntity> GetEntities(Common.EntityType entityType)
{
List<IEntity> entities = new List<IEntity>();
switch (entityType)
{
case Common.EntityType.Accounts:
Entity1 entity1 = new Entity1();
entity1.Property1 = "AA";
entities.Add(entity1);
break;
case Common.EntityType.Brands:
Entity2 entity2 = new Entity2();
entity2.Property2 = "AA";
entities.Add(entity2);
break;
default:
break;
}
return entities;
}
UIから、私はそれをこのように呼びます
BusinessClass b = new BusinessClass();
List<IEntity> a = b.GetEntities(Common.EntityType.Accounts);
お役に立てれば