Приведение общего типа элемента вниз - PullRequest
7 голосов
/ 07 февраля 2011
public class ConfigControlBase<T> : UserControl
    where T : ProviderBase
{
    public T Provider { get; set; }

    public void Init(T provider)
    {
        this.Provider = provider;
    }
}


public abstract class ProviderBase
{
    public abstract ConfigControlBase<ProviderBase> GetControl();
}

public class ProviderXConfigControl : ConfigControlBase<ProviderX>
{
}

public class ProviderX : ProviderBase
{
    public override ConfigControlBase<ProviderBase> GetControl()
    {
        var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>;
        return confControl;
    }
}

return confControl; выдает исключение:

Невозможно неявно преобразовать тип ConfigControlBase<ProviderX> в ConfigControlBase<ProviderBase>

Ответы [ 5 ]

21 голосов
/ 07 февраля 2011

Давайте изменим имя ваших классов и свойств, но сохраним форму:

public class Cage<T> where T : Animal
{
    public T Contents { get; set; }
}

public class Aquarium : Cage<Fish> { }

public abstract class Animal
{
    public abstract Cage<Animal> GetCage();
}

public class Fish : Animal
{
    public override Cage<Animal> GetCage()
    {
        return (Cage<Animal>)(new Aquarium());
    }
}

Теперь понятно, почему это не законно? Предположим, это было законно. Тогда вы могли бы сделать это:

Fish fish = new Fish();
Cage<Animal> cage = fish.GetCage();
cage.contents = new Tiger();

А теперь у тебя в аквариуме тигр. И никто этого не хочет.

Компилятор (или среда выполнения) должен как-то предотвратить ошибку этого типа; он решает предотвратить это как можно скорее. Самое раннее, что он может сделать, - это тестирование типа для преобразования из Аквариума в Cage<Animal>. Компилятор знает, что это может в конечном итоге привести к тиграм в аквариумах, поэтому он вообще не допускает конвертацию Если вы заставите компилятор разрешить его через приведение типов, то он выйдет из строя во время выполнения.

8 голосов
/ 07 февраля 2011

Универсальные типы с аргументами присваиваемого типа сами по себе не присваиваются.
Например, вы не можете разыграть List<string> до List<object>, хотя string - это object.

Не сразу понятно, почему такое преобразование не поддерживается, поэтому приведу пример:

var words = new List<string> { "Serve God", "love me", "mend" };
var objects = (List<object>) words; // C# compiler wouldn't allow this
objects.Add (new Car()); // we just added a Car to Shakespeare's work and the universe exploded

C # не способствует взрыву вселенной, однако, начиная с C # 4.0, реализована облегченная версия этой идеи. Видите ли, в некоторых случаях такое приведение было бы в действительности безопасным.

.NET 4.0 содержит понятия ковариации и контравариантности в обобщениях только для интерфейсов и делегатов , вы можете проверить это.

Пример (не работает до .NET 4.0):

void HandleCollection (IEnumerable<object> collection)
{
    // ...
}

var words = new List<string> { "Serve God", "love me", "mend" };

// IEnumerable is defined as IEnumerable<out T> in .NET 4.0
// 'out' keyword guarantees that T is only used for return values
// and therefore client code can't explode the universe   

var objects = (IEnumerable<object>) words;
HandleCollection (objects);
1 голос
/ 07 февраля 2011

Это потому, что ConfigControlBase<ProviderX> не ConfigControlBase<ProviderBase>

0 голосов
/ 02 июля 2011

Этот ответ может быть бесполезен в вашем сценарии, поскольку вам, вероятно, следует искать другое решение, но во время размышлений я нашел возможность приводить к менее универсальным типам очень полезной , поэтому я написал решение дляЭто.Однако он работает только для интерфейсов, и вы должны гарантировать, что будете передавать в интерфейс только объекты правильных типов.

Я в основном создаю прокси-класс во время выполнения , который выполняет всенеобходимые касты для вас.Его использование выглядит следующим образом:

object validator;  // An object known to implement IValidation<T>.
object toValidate; // The object which can be validated by using the validator.

// Assume validator is IValidation<string> and toValidate a string.

IValidation<object> validation
    = Proxy.CreateGenericInterfaceWrapper<IValidation<object>>( validator );

validation.IsValid( toValidate ); // This works! No need to know about the type.

// The following will throw an InvalidCastException.
//validation.IsValid( 10 );

Более подробную информацию и исходный код можно найти в моем блоге .

0 голосов
/ 07 февраля 2011

ваш

 public override ConfigControlBase<ProviderBase> GetControl()

не соответствует

var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...