web-dev-qa-db-ja.com

C <List <ObjBase>をList <Obj>として​​キャストする

List<ObjBase>List<Obj>としてキャストできないのはなぜですか?以下が機能しない理由:

internal class ObjBase
   {
   }

internal class Obj : ObjBase
   {
   }   

internal class ObjManager
{
    internal List<Obj> returnStuff()
    {
       return getSomeStuff() as List<Obj>;
    }

    private List<ObjBase> getSomeStuff()
    {
       return new List<ObjBase>();
    }

}

代わりに私はこれをしなければなりません:

internal class ObjBase
   {
   }

internal class Obj : ObjBase
   {
   }

internal class ObjManager
{
    internal List<Obj> returnStuff()
    {
       List<ObjBase> returnedList = getSomeStuff();
       List<Obj> listToReturn = new List<Obj>(returnedList.Count);
       foreach (ObjBase currentBaseObject in returnedList)
       {
          listToReturn.Add(currentBaseObject as Obj);
       }
       return listToReturn;
    }

    private List<ObjBase> getSomeStuff()
    {
       return new List<ObjBase>();
    }
}

Visual Studio 2008で次のエラーが表示されます(読みやすくするために省略)。

参照変換、ボックス化変換、ボックス化解除変換、ラッピング変換、またはnull型変換を介して、型 'リスト'を 'リスト'に変換できません

ありがとう。

26
AndrewJacksonZA
8
gjutras

System.LinqのCastおよびToList拡張メソッドを使用して、これを1行で記述できます。

の代わりに

internal List<Obj> returnStuff()
{
   return getSomeStuff() as List<Obj>;
}

これを行う:

internal List<Obj> returnStuff()
{
   return getSomeStuff().Cast<Obj>().ToList();
}
34
RaYell

「問題」はJava=ビューからのみ説明できますが、C#とJavaの両方でこの側面が同じであることはほとんどわかっていません。

_List<ObjBase>_は_List<Obj>_ではありません。これは、ObjBaseオブジェクトではないObjオブジェクトを含む可能性があるためです。

_List<Obj>_の逆の方法は、__notを_List<ObjBase>_にキャストできます。前者はObjBase引数を使用したAdd()呼び出しの受け入れを保証するためです。後者は受け付けません!

つまり、ObjObjBaseですが、_List<Obj>_はnot a _List<ObjBase>_です。

10
Joachim Sauer
3
Dewfy

あなたがしようとしているキャストを誤解していると思います。リストに格納されているオブジェクトのタイプを変更しようとしていると考えていますが、実際にはリスト自体のタイプを変更しようとしています。既に入力済みなので、リスト自体を変更できないのは当然です。

あなたはそれを基本クラスのリストとして見て、リストの項目を処理しているときにそれをキャストするかもしれません、それが私のアプローチでしょう。

このキャストの目的は何ですか?

1
Lazarus

C#は現在、ジェネリック型のバリアンスをサポートしていません。私が読んだことから、これは4.0で変更されます。

ジェネリックの分散の詳細については、 ここ を参照してください。

1
Scott Ivey

list.ConvertAllは魅力的に見えますが、1つの大きな欠点があります。まったく新しいリストが作成されます。これは、特に大きなリストのパフォーマンスとメモリ使用量に影響します。

もう少し努力して、元のリストを内部参照として保持するラッパーリストクラスを作成し、それらが使用される場合にのみアイテムを変換できます。

使用法:

var x = new List<ObjBase>();
var y = x.CastList<ObjBase, Obj>(); // y is now an IList<Obj>

ライブラリに追加するコード:

public static class Extensions
{
    public static IList<TTo> CastList<TFrom, TTo>(this IList<TFrom> list)
    {
        return new CastedList<TTo, TFrom>(list);
    }
}

public class CastedList<TTo, TFrom> : IList<TTo>
{
    public IList<TFrom> BaseList;

    public CastedList(IList<TFrom> baseList)
    {
        BaseList = baseList;
    }

    // IEnumerable
    IEnumerator IEnumerable.GetEnumerator() { return BaseList.GetEnumerator(); }

    // IEnumerable<>
    public IEnumerator<TTo> GetEnumerator() { return new CastedEnumerator<TTo, TFrom>(BaseList.GetEnumerator()); }

    // ICollection
    public int Count { get { return BaseList.Count; } }
    public bool IsReadOnly { get { return BaseList.IsReadOnly; } }
    public void Add(TTo item) { BaseList.Add((TFrom)(object)item); }
    public void Clear() { BaseList.Clear(); }
    public bool Contains(TTo item) { return BaseList.Contains((TFrom)(object)item); }
    public void CopyTo(TTo[] array, int arrayIndex) { BaseList.CopyTo((TFrom[])(object)array, arrayIndex); }
    public bool Remove(TTo item) { return BaseList.Remove((TFrom)(object)item); }

    // IList
    public TTo this[int index]
    {
        get { return (TTo)(object)BaseList[index]; }
        set { BaseList[index] = (TFrom)(object)value; }
    }

    public int IndexOf(TTo item) { return BaseList.IndexOf((TFrom)(object)item); }
    public void Insert(int index, TTo item) { BaseList.Insert(index, (TFrom)(object)item); }
    public void RemoveAt(int index) { BaseList.RemoveAt(index); }
}

public class CastedEnumerator<TTo, TFrom> : IEnumerator<TTo>
{
    public IEnumerator<TFrom> BaseEnumerator;

    public CastedEnumerator(IEnumerator<TFrom> baseEnumerator)
    {
        BaseEnumerator = baseEnumerator;
    }

    // IDisposable
    public void Dispose() { BaseEnumerator.Dispose(); }

    // IEnumerator
    object IEnumerator.Current { get { return BaseEnumerator.Current; } }
    public bool MoveNext() { return BaseEnumerator.MoveNext(); }
    public void Reset() { BaseEnumerator.Reset(); }

    // IEnumerator<>
    public TTo Current { get { return (TTo)(object)BaseEnumerator.Current; } }
}
1
Bigjim

LinqにはConvertAllメソッドがあります。のようなもの

list.ConvertAll<Obj>(objBase => objbase.ConvertTo(obj));

他に何を提案すればよいかわかりません。私はObjBaseが基本クラスであると想定しています。すべてのObjBaseオブジェクトがObjオブジェクトである場合、なぜ最初に2つのオブジェクトがあるのか​​わかりません。たぶん私は適格ではありません。

編集:list.Castメソッドは、お互いにキャスト可能であると仮定して、上記よりもうまく機能します。他の回答を読むまでは、そのことを忘れていました。

0
Jimmeh

ラザロ
コンパイラーは、リストのオブジェクトに対してアクションを実行することを望んでいるのではなく、リスト自体をキャストしようとしているのではないことに気付くと思いました。

その他の情報:

public abstract class ObjBase
   {
   }

internal interface IDatabaseObject
   {
   }

public class Obj : ObjBase, IDatabaseObject
   {
   }


internal interface IDatabaseObjectManager
   {
      List<ObjBase> getSomeStuff();
   }

public class ObjManager : IObjManager
{
    public List<Obj> returnStuff()
    {
       return getSomeStuff().Cast <Customer>().ToList<Customer>();
    }

    private List<ObjBase> getSomeStuff()
    {
       return new List<ObjBase>();
    }
}

これ以外のクライアントコードDLL go go:ObjManager objM = new ObjManager(); List listOB = objM.returnStuff();私はいくつかのObjを作成しますアプリケーションのこの部分(O/RM)のObjManagerタイプ。

(コメントブロックが文字を使い果たしました!:-)

0
AndrewJacksonZA

これはC#の大きな問題です-これがジェネリックの設計方法です。リストはリストを拡張せず、完全に異なるタイプです。キャストしたり、相互に割り当てたりすることはできません。唯一の選択肢は、1つのリストを別のリストにコピーすることです。

0
Grzenio

ここから私は変換を修正した方法です

list<SomeOtherObject>

object

そして次に

List<object>

https://stackoverflow.com/a/16147909/2307326

0
Andrew Marais