cast作为泛型接口返回null

时间:2016-12-16 11:45:47

标签: c# generics

我有一个界面:

public interface IReminder<T> where T : class, IIdentifiableEntity
{
    IEnumerable<T> GetRemindersToBeSent(IRepository<T> repository);

}

和实现此接口的类TimesheetReminder

public class TimesheetReminder : IReminder<InvoiceSummary>
{

    public IEnumerable<InvoiceSummary> GetRemindersToBeSent(IRepository<InvoiceSummary> invoiceSummaryRepository)
    {
        var today = DateTime.Today;
        return invoiceSummaryRepository.List.Where(inv =>
            inv.InvoiceSummaryStatus.CKAStatusName == "Draft" &&
            inv.InsertDateUTC <= today.AddDays(-3)            &&
            inv.InsertDateUTC >= today.AddDays(-6)            &&
            inv.EndDate       <= today.AddDays(-3)
        );
    }

InvoiceSummary实施IIdentifyableEntity,但

public static class ReminderFactory<T> where T : class, IIdentifiableEntity
{
    public static IReminder<T> GetReminder(string applicationType)
    {
        IReminder<T> reminder;

        switch (applicationType)
        {
            case "Invoicing":
                reminder = (IReminder<T>)new TimesheetReminder();
                break;
            default:
                reminder = null;
                break;
        }

        return reminder;
    }
}

开票案例返回null。

如果TimesheetReminder未实现IReminder的{​​{1}}我会理解,但确实如此。

我做错了什么?

4 个答案:

答案 0 :(得分:4)

什么是TTimeSheetReminderIReminder<InvoiceSummary>,因此如果T不是InvoiceSummary,则无法进行参考转换:

class Foo: IIdentifiableEntity { ... }
var reminder = new TimesheetReminder() as IReminder<Foo>; //returns null

答案 1 :(得分:1)

尝试以下....

IReminder<InvoiceSummary> reminder = new TimesheetReminder() as IReminder<InvoiceSummary>; 

答案 2 :(得分:0)

我认为您通过传递 IIdentifiableEntity 界面来创建工厂实例,如下所示:

var factory = ReminderFactory<IIdentifiableEntity>.GetReminder("Invoicing");

即使我们输入强制转换

,也会始终返回 null
reminder = new TimesheetReminder() as IReminder<T>;

<强>解决方案:

在创建工厂实例时,我们需要传递 InvoiceSummary

的具体类类型
var factory = ReminderFactory<InvoiceSummary>.GetReminder("Invoicing");

即使我们可以通过删除硬编码字符串来修改创建工厂实例&#34; Invoicing&#34;

var factory = ReminderFactory.Create<InvoiceSummary>();

示例代码如下所示:

public class Program
{
    static void Main(string[] args)
    {
        //Hp --> Logic: Create factory instance by passing concrete class type.
        var factory = ReminderFactory.Create<InvoiceSummary>();
        var x = factory.GetRemindersToBeSent(new InvoiceRepository());
        x.ToList().ForEach(item =>
        Console.WriteLine($"{item.EntityName}:{item.TotalAmount}"));

        Console.ReadKey();
    }
}

public interface IIdentifiableEntity
{
    string EntityName { get; set; }
}

public static class ReminderFactory
{
    public static IReminder<T> Create<T>() where T : IIdentifiableEntity
    {
        IReminder<T> reminder = null;
        if (typeof(InvoiceSummary) == typeof(T))
        {
            reminder = new TimesheetReminder() as IReminder<T>;
        }

        return reminder;
    }
}

public interface IReminder<T> where T : IIdentifiableEntity
{
    IEnumerable<T> GetRemindersToBeSent(IRepository<T> repository);
}

public interface IRepository<T>
{
    IEnumerable<T> List { get; }
}

public class InvoiceRepository : IRepository<InvoiceSummary>
{
    public IEnumerable<InvoiceSummary> List => new List<InvoiceSummary> {
        new InvoiceSummary { EntityName = "Invoice", TotalAmount = 100.00M } };
}

public class InvoiceSummary : IIdentifiableEntity
{
    public string EntityName { get; set; }
    public decimal TotalAmount { get; set; }
}

public class TimesheetReminder : IReminder<InvoiceSummary>
{
    public IEnumerable<InvoiceSummary> GetRemindersToBeSent(
        IRepository<InvoiceSummary> repository) =>
        repository.List.Where(I => IsEqual("Invoice", I.EntityName));

    private bool IsEqual(string source, string target) =>
       string.Equals(source, target, StringComparison.CurrentCultureIgnoreCase);
}

答案 3 :(得分:0)

出于任何其他原因,如果您仍想在创建工厂实例时传递界面( IIdentifiableEntity

var factory = ReminderFactory<IIdentifiableEntity>.GetReminder("Invoicing");

解决方案(概念证明):我不喜欢,因为它设计不好。

  1. 我们需要将接口 (IReminder,IRepository) 更改为covaraint。

  2. 在阅读IIdentifiableEntity数据时,我们需要转换为相应的concreate类类型。

  3. 示例代码如下所示:请遵循代码注释

    public class Program
    {
        static void Main(string[] args)
        {
            //Hp --> Note: While creating factory instance we are passing interface type.
            var factory = ReminderFactory<IIdentifiableEntity>.GetReminder("Invoicing");
            var x = factory.GetRemindersToBeSent(new InvoiceRepository());
    
            //Hp --> Note: While reading data we need to cast to corrsponding concreate class type.
            x.Cast<InvoiceSummary>().ToList().ForEach(item =>
            Console.WriteLine($"{item.EntityName}:{item.TotalAmount}"));
            Console.ReadKey();
        }
    }
    
    public interface IRepository<out T>
    {
        // Hp --> Note: You can't use setter since T it is out parameter (covariant)
        IEnumerable<T> List { get; }
    }
    
    public interface IReminder<out T> where T : class, IIdentifiableEntity
    {
        //Hp --> Note: You can't use IRepository<T> here since T is out parameter (covariant)
        //Instead of T use interface IIdentifiableEntity
        IEnumerable<T> GetRemindersToBeSent(IRepository<IIdentifiableEntity> repository);
    }
    
    public class TimesheetReminder : IReminder<InvoiceSummary>
    {
        public IEnumerable<InvoiceSummary> GetRemindersToBeSent(
            //Hp --> Note: We need to cast IIdentifiableEntity to corrsponding concreate class type.
            IRepository<IIdentifiableEntity> repository) =>
            repository.List.Where(I => IsEqual("Invoice", I.EntityName)).Cast<InvoiceSummary>();
    
        private bool IsEqual(string source, string target) =>
           string.Equals(source, target, StringComparison.CurrentCultureIgnoreCase);
    }
    
    public interface IIdentifiableEntity
    {
        string EntityName { get; set; }
    }
    
    public static class ReminderFactory<T> where T : class, IIdentifiableEntity
    {
        public static IReminder<T> GetReminder(string applicationType)
        {
            IReminder<T> reminder;
            switch (applicationType)
            {
                case "Invoicing":
                    reminder = new TimesheetReminder() as IReminder<T>;
                    break;
                default:
                    reminder = null;
                    break;
            }
            return reminder;
        }
    }
    
    public class InvoiceRepository : IRepository<InvoiceSummary>
    {
        public IEnumerable<InvoiceSummary> List => new List<InvoiceSummary> {
            new InvoiceSummary { EntityName = "Invoice", TotalAmount = 100.00M } };
    }
    
    public class InvoiceSummary : IIdentifiableEntity
    {
        public string EntityName { get; set; }
        public decimal TotalAmount { get; set; }
    }