Как я могу перечислить через объект типа `T`, когда это IEnumerable - PullRequest
5 голосов
/ 14 июня 2011

Это немного сложно объяснить.Итак, все идет.

У меня есть такая функция:

public T FooBar<T>(Func<T> function)
{
   T returnData = function();

   // want to iterate through returnData to do something to it

   return returnData;
}

Если returnData (T) - это список IEnumerable, то я бы хотел перечислить через returnData чтобы изменить его содержимое, используя отражение.Но я не могу этого сделать.Когда я пытаюсь привести returnData к перечисляемому типу, я получаю исключение:

Невозможно привести объект типа

'System.Collections.Generic.List`1 [Автомобили] '

для ввода

' System.Collections.Generic.List`1 [System.Object] '.

Я не буду знать, что возвратТип будет список «автомобилей», например, заранее, только во время выполнения.Поэтому я должен с помощью рефлексии проверить, является ли он списком, и затем попытаться составить его, чтобы я мог перечислить его.

Если я не поступлю неправильно.Как я могу перечислить через returnData, если оно имеет тип T?

Ответы [ 4 ]

6 голосов
/ 14 июня 2011

Один из подходов - добавить ограничение типа на T, но это не идеально:

public T FooBar<T>(Func<T> function) where T : IEnumerable
{
    // T is not strongly typed for the enumerated item

Если вы немного изменили свой метод (w.r.t. T):

public IEnumerable<T> FooBar<T>(Func<IEnumerable<T>> function)

Затем вы строго печатаете на фактическом перечисляемом элементе с дополнительным бонусом принятия перечисляемых объектов.


Итак, после второго прочтения вашего вопроса я заметил, что существует некоторая путаница в том, что означает T для вашей переменной returnData. В случае, когда передается FooBar(), List<Car>, T равен List<Car> и действительно не имеет связи с общей спецификацией типа самой List<>. Вы можете думать об этом как о некотором List<U>, где U - некоторый другой, неизвестный тип.

Во время выполнения у вас не будет простого способа добраться до U, поскольку он, так сказать, скрыт внутри T. Вы можете использовать перегрузку, как рекомендуют некоторые другие ответчики, и предоставить метод, отличный от IEnumerable<U>, и метод, который принимает аргументы типа Func<IEnumerable<T>>.

Возможно, с некоторыми подробностями о цели FooBar<T>, мы могли бы дать более конкретные рекомендации.

4 голосов
/ 14 июня 2011
if (returnData is System.Collections.IEnumerable)
{
   foreach (object o in (System.Collections.IEnumerable)returnData)
   {
      // Do something.
   }
}

Действительно, почему бы не иметь дополнительную перегрузку, подобную этой:

public T FooBar<T>(Func<IEnumerable<T>> function) 
2 голосов
/ 14 июня 2011

Вы пробовали приведение типа к IEnumerable вместо IEnumerable<T>? С IEnumerable вы все равно можете использовать его в цикле foreach. Переменная, в которую будет входить каждый элемент, должна иметь тип object, т. Е .:

foreach(object item in (IEnumerable)T){...}

Сначала убедитесь, что T реализует IEnumerable.

1 голос
/ 14 июня 2011

Проблема здесь в том, что IEnumerable и IEnumerable Of T не одинаковы ... но вы можете проверить разницу и учесть это в своем коде.Обратите внимание, что IEnumerable Of T наследует IEnumerable, поэтому вы можете заключить проверку для универсальной версии внутри неуниверсальной версии.

Следующее сработало для меня в небольшом тесте, который я написал - надеюсь, вам достаточно сделать то, что вам нужно.

Вот мясо и картофель:

class FooBarOfT
{
    public T FooBar<T>(Func<T> function)
    {
        T returnData = function();

        //Want to iterate through returnData to do something to it.
        if (returnData is IEnumerable) 
        {
            // get generic type argument
            var returnDataType = returnData.GetType();

            if (returnDataType.IsGenericType)
            {
                // this is a System.Collections.Generic.IEnumerable<T> -- get the generic type argument to loop through it
                Type genericArgument = returnDataType.GetGenericArguments()[0];

                var genericEnumerator =
                    typeof(System.Collections.Generic.IEnumerable<>)
                        .MakeGenericType(genericArgument)
                        .GetMethod("GetEnumerator")
                        .Invoke(returnData, null);

                IEnumerator enm = genericEnumerator as IEnumerator;
                while (enm.MoveNext())
                {
                    var item = enm.Current;
                    Console.WriteLine(string.Format("Type : {0}", item.GetType().Name));
                }

            }
            else
            {
                // this is an System.Collections.IEnumerable (not generic)
                foreach (var obj in (returnData as IEnumerable))
                {
                    // do something with your object
                }
            }
        }

        return returnData;
    }
}

Я также настроил несколько вспомогательных классов тестирования:

class Foo
{
    private string _fooText;

    public Foo(string fooText)
    {
        _fooText = fooText;
    }
    public string Execute()
    {
        return string.Format("executed! with {0} !", _fooText);
    }
}

class Bar
{
    public string BarContent { get; set; }
}

И небольшое консольное приложение для запуска некоторых тестов:

class Program
{
    static void Main(string[] args)
    {
        // tests
        Func<string> stringFunc = () => 
            "hello!";

        Func<List<Foo>> listFooFunc = () => 
            new List<Foo> 
            { 
                new Foo("Hello!"),
                new Foo("World!")
            };

        Func<IEnumerable> ienumerableFooFunc = () =>
            new Hashtable
            {
                { "ItemOne", "Foo" },
                { "ItemTwo", "Bar" }
            };


        var fooBarOfT = new FooBarOfT();

        fooBarOfT.FooBar(stringFunc);
        fooBarOfT.FooBar(listFooFunc);
        fooBarOfT.FooBar(ienumerableFooFunc);

        Console.ReadKey();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...