4

この質問でのJon Skeetの回答のおかげで、次の作業ができました。

public delegate BaseItem GetItemDelegate(Guid itemID);

public static class Lists
{
  public static GetItemDelegate GetItemDelegateForType(Type derivedType)
  {
    MethodInfo method = typeof(Lists).GetMethod("GetItem");
    method = method.MakeGenericMethod(new Type[] { derivedType });
    return (GetItemDelegate)Delegate.CreateDelegate(typeof(GetItemDelegate), method);
  }

  public static T GetItem<T>(Guid itemID) where T : class { // returns an item of type T ... }
}

public class DerivedItem : BaseItem { }

// I can call it like so:
GetItemDelegate getItem = Lists.GetItemDelegateForType(typeof(DerivedItem));
DerivedItem myItem = getItem(someID); // this works great

戻り値の型とオーバーロードが異なるメソッドに同じことを適用しようとすると (これらは私が思いつく唯一の違いです)、迷惑な "ArgumentException: ターゲット メソッドへのバインディング エラー" が発生します。への通話中CreateDelegate。以下は、エラーを取得する実際の例です。コンソール アプリにコピー/貼り付けするだけです。

public delegate IEnumerable<BaseItem> GetListDelegate();

public class BaseItem { }
public class DerivedItem : BaseItem { }

public static class Lists
{
  public static GetListDelegate GetListDelegateForType(Type itemType)
  {
    MethodInfo method = typeof(Lists).GetMethod("GetList", Type.EmptyTypes); // get the overload with no parameters
    method = method.MakeGenericMethod(new Type[] { itemType });
    return (GetListDelegate)Delegate.CreateDelegate(typeof(GetListDelegate), method);
  }

  // this is the one I want a delegate to, hence the Type.EmptyTypes above
  public static IEnumerable<T> GetList<T>() where T : class { return new List<T>(0); }
  // not the one I want a delegate to; included for illustration
  public static IEnumerable<T> GetList<T>(int param) where T : class { return new List<T>(0); }

  public static Type GetItemType()
  { // this could return any type derived from BaseItem
    return typeof(DerivedItem);
  }
}

class Program
{
  static void Main(string[] args)
  {
    Type itemType = Lists.GetItemType();
    GetListDelegate getList = Lists.GetListDelegateForType(itemType);
    IEnumerable<BaseItem> myList = (IEnumerable<BaseItem>)getList();
  }
}

上記のように、私が見ることができる唯一の違いは次のとおりです。

  1. 異なる戻り値の型 (T動作しますが、IEnumerable<T>動作しません) [編集: これは正しくありません。最初のバージョンではBaseItem、 ではなくが使用されていTます。おっとっと]
  2. オーバーロード (オーバーロードGetItemはありませんが、GetListいくつかあります。GetList()パラメーターのないデリゲートのみが必要です

Update1: Sam は、いくつかの問題を特定するのを手伝ってくれました。デリゲートの戻り値の型がジェネリック (例: IEnumerable<BaseItem>) の場合、基本型と派生型を交換しようとすると窒息します。以下のようにメソッドを宣言するGetList方法はありますか? Tがから継承されていることを示すことができる必要がありますがBaseItem、それができれば問題なく動作します。

public static IEnumerable<BaseItem> GetList<T>() where T : class

もう 1 つのオプションは、デリゲート宣言を「ジェネリック化」することです。私が見つけることができるすべての例は、戻り値の型ではなく、params のジェネリックを使用しています。これを行うにはどうすればよいですか (原因が定義されていないコンパイラ エラーがスローされ、制約Tを使用できません):where

public delegate IEnumerable<T> GetListDelegate();
4

2 に答える 2

2

デリゲートを just として宣言することで、これが機能するようになりましたIEnumerable。これにより、デリゲートを作成できます。その時残っていたのは基本的なキャスティングだけでした。以下の変更により、上記の 2 番目のコード ブロックが修正されます。

// declare this as non-generic
public delegate IEnumerable GetListDelegate();

// do some cast-fu to get the list into a workable form
List<BaseItem> myList = getList().Cast<BaseItem>().ToList();

その後myList.Sort()、作業中のシステムで実行しようとしている他のすべてのことを実行できます。

于 2010-01-14T20:49:07.190 に答える
1

2 番目の例をコンパイルするためにいくつかの小さな変更を加えた後、それを実行することができ、デリゲートを正常に取得して呼び出すことができました。

using System;
using System.Collections.Generic;
using System.Reflection;

namespace ConsoleApplication1
{
    public delegate IEnumerable<BaseItem> GetListDelegate();

    public class BaseItem { }
    public class DerivedItem : BaseItem { }

    public static class Lists
    {
        public static GetListDelegate GetListDelegateForType(Type derivedType)
        {
            MethodInfo method = typeof(Lists).GetMethod("GetList", Type.EmptyTypes); // get the overload with no parameters
            method = method.MakeGenericMethod(new[] { derivedType });
            return (GetListDelegate)Delegate.CreateDelegate(typeof(GetListDelegate), method); // *** this throws an exception ***
        }

        // this is the one I want a delegate to, hence the Type.EmptyTypes above
        public static IEnumerable<T> GetList<T>() where T : class
        {// returns a collection of T items ... 
            return new T[0];
        }

        // not the one I want a delegate to; included for illustration, maybe my different GetMethod() is my problem?
        public static IEnumerable<T> GetList<T>(int param) where T : class
        { // returns a collection of T items ... 
            return new T[0];
        }
    }

    public class GenericDelegate
    {
        public static void Test()
        {

            // I would call it like so, but first line gets exception, where indicated above
            GetListDelegate getList = Lists.GetListDelegateForType(typeof(BaseItem));
            IEnumerable<BaseItem> myList = getList();
        }
    }
}

ただし、2番目の例をコンパイルする方法がわかりません。ここに問題があるようです。

public delegate IEnumerable<BaseItem> GetListDelegate();

GetListDelegate getList = Lists.GetListDelegateForType(typeof(DerivedList));
IEnumerable<DerivedList> myList = getList();

デリゲートは IEnumerable を返すように宣言されていますが、それを呼び出して結果を IEnumerable に代入します。これは C# 3.5 ではサポートされていません。それはC#4にありますが、共分散(または反分散、どちらかわかりません)を宣言するには、BaseItem / DerivedListを別の方法で宣言する必要があります。

于 2010-01-14T04:38:02.613 に答える