Почему этот пример C # не работает? - PullRequest
5 голосов
/ 06 марта 2012

Я пытаюсь понять, почему этот состав не работает:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CastTest {

    class Foo {}

    // I want to use this kind of like a typedef, to avoid writing List<Foo> everywhere.
    class FooList : List<Foo> {}

    class Program {
        static void Main(string[] args) {
            FooList list = (FooList) Program.GetFooList();
        }

        // Suppose this is some library method, and i don't have control over the return type
        static List<Foo> GetFooList() {
            return new List<Foo>();
        }
    }
}

Это генерирует ошибку во время выполнения:

InvalidCastException: невозможно привести объект типа 'System.Collections.Generic.List`1 [CastTest.Foo]' к типу CastTest.FooList.

Может кто-нибудь объяснить, почему это не работает, и могу ли я как-нибудь обойти это?

Ответы [ 7 ]

7 голосов
/ 06 марта 2012

Тот факт, что ваш FooList является списком, не делает список списком FooList.

5 голосов
/ 06 марта 2012

Вы не можете автоматически приводить из родительского класса к производному классу: просто потому, что объект, который вы пытаетесь привести, является List<Foo>, не обязательно делает его FooList.

Выезд:

http://social.msdn.microsoft.com/Forums/is/csharplanguage/thread/f7362ba9-48cd-49eb-9c65-d91355a3daee

Вы можете написать оператор, который преобразует List<Foo> в FooList, возможно, используя AddRange для заполнения значений, например:

class FooList
{
 public explicit operator FooList(List<Foo> arg)
 {
   FooList result = new FooList();
   result.AddRange(arg);
   return result;
  }
}

Также может быть лучше просто использовать using псевдоним : http://msdn.microsoft.com/en-us/library/sf0df423(v=vs.80).aspx

using FooList = List<Foo>;

Таким образом, вы фактически не пропускаете ненужные производные классы.

4 голосов
/ 06 марта 2012

Этот метод:

static List<Foo> GetFooList() {
    return new List<Foo>();
}

... не возвращает FooList. Ваш код по сути такой:

FooList list = (FooList) new List<Foo>();

Это просто не сработает. Это так же неверно, как:

string x = (string) new object();

Вы бы не ожидали, что это сработает? Так почему же ваша FooList версия работает?

2 голосов
/ 06 марта 2012

Потому что не все List являются FooList. Например, я мог бы иметь:

public class AnotherFooList : List<Foo>
{
    public object AdditionalPropery {get; set; }
}

Он также наследуется от List, но не совпадает с FooList, а FooList не совпадает с ним.

2 голосов
/ 06 марта 2012

Ближайший C # к typedef называется директивой using alias:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CastTest {
    using FooList = List<Foo>;

    class Foo {}

    class Program {
        static void Main(string[] args) {
            FooList list = Program.GetFooList();
        }

        // Suppose this is some library method, and i don't have control over the return type
        static List<Foo> GetFooList() {
            return new List<Foo>();
        }
    }
}

То, что у вас есть, создает производный класс FooList, но это создает псевдоним FooList.Он не совместим с List<Foo>, он равен List<Foo>.

1 голос
/ 06 марта 2012

Вы не можете upcast;но вы можете создать новый экземпляр FooList и его содержимое

class Foo { }

    // I want to use this kind of like a typedef, to avoid writing List<Foo> everywhere.
    class FooList : List<Foo> {}

    class Program
    {
        static void Main(string[] args)
        {
            FooList l = new FooList();
            l.AddRange(GetFooList().Select(foo => foo));

            Console.ReadLine();
        }

        // Suppose this is some library method, and i don't have control over the return type
        static List<Foo> GetFooList()
        {
            return new List<Foo>();
        }
    }
1 голос
/ 06 марта 2012

Поскольку вы знаете, что FooList на самом деле является просто другим именем для List<Foo>, это может показаться странным. Но, учитывая, что FooList может содержать сами члены, становится очевидным, что компилятор не должен разрешать приведение. Представьте, что вы позже представите метод (Bar()) для FooList. Этот метод отсутствует в List<Foo>, и система типов просто сломалась бы, если присваивание (или приведение) было разрешено.

...