Существуют ли какие-либо правила для преобразования типов между генераторами С#?
У меня есть интерфейс IJob, из которого наследуются все классы Job, и у меня есть общий класс repo, который обработает все виды IJob.
Проблема im сталкивается с тем, что невозможно преобразовать Repository<Job1>
в Repository<IJob>
, хотя Job1
является типом IJob
.
код
internal class Program
{
public interface IJob { }
public class Job1 : IJob { }
public class Job2 : IJob { }
public class Repository<TJob>
where TJob : IJob
{
public List<TJob> GetJobs()
{
return new List<TJob>();
}
}
private static void Main(string[] args)
{
IJob iJob = new Job1(); // Valid
Repository<IJob> repo = new Repository<Job1>(); // Not Valid
}
}
Может кто-нибудь сообщить мне, почему это невозможно в С# и есть ли какие-либо другие преобразования типов правил в С# generics?
Ответы
Ответ 1
Если вы хотите ковариацию, вам придется ввести общий интерфейс для репозитория и изменить тип возврата метода GetJobs
на IEnumerable<TJob>
:
internal class Program
{
public interface IJob { }
public class Job1 : IJob { }
public class Job2 : IJob { }
public interface IRepository<out TJob> where TJob : IJob
{
IEnumerable<TJob> GetJobs();
}
public class Repository<TJob> : IRepository<TJob> where TJob : IJob
{
public IEnumerable<TJob> GetJobs()
{
return new List<TJob>();
}
}
private static void Main(string[] args)
{
IJob iJob = new Job1(); // Valid
IRepository<IJob> repo = new Repository<Job1>(); // Also valid
}
}
Для получения дополнительной информации о ковариации и контравариантности см.: https://msdn.microsoft.com/en-us/library/dd799517%28v=vs.110%29.aspx
Ответ 2
Repository<IJob> repo = new Repository<Job1>(); // Not Valid
Да, потому что это не Repository<IJob>
. С помощью Repository<IJob>
эта строка будет действительна:
repo.GetJobs().Add(new Job2());
Теперь, с Repository<Job1>
, эта строка недействительна. Он не будет компилироваться.
Итак, ошибка, которую вы получаете, верна. A Repository<Job1>
не a Repository<IJob>
.
Ответ 3
Вы просто хотите
Repository<IJob> repo = new Repository<IJob>();
то вы можете добавить любой IJob
в репозиторий.
repo.Add(new Job1());
repo.Add(new Job2());
Ответ 4
чтобы понять, почему этого не может быть сделано, нам нужно вернуться к тому, что означает быть подклассом объекта в С#. когда у вас есть такая ситуация:
public class A
{
public A GetInstance { get;set;}
}
public class B : A
{
}
класс B наследует все методы из класса A, включая подпись метода. на самом деле вы можете создать экземпляр B, который имеет метод GetInstance ThisReturns A, но не B. Если вы попытаетесь переопределить метод GetInstance для возврата B, он, конечно, даст ошибку компиляции (метод не может быть исключен, если только изменение - это тип возврата).
с использованием обобщений немного отличается, потому что изменение типа ввода приведет к изменению сигнатуры всех методов.
Проще говоря, даже если Job1 является подклассом IJob, методы класса Repository (IJob) имеют различную сигнатуру методов класса Repository (Job1), поэтому они не связаны друг с другом, но как два отдельных класса.