我有一个界面:
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}}我会理解,但确实如此。
我做错了什么?
答案 0 :(得分:4)
什么是T
? TimeSheetReminder
为IReminder<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");
即使我们输入强制转换
,也会始终返回 nullreminder = 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");
解决方案(概念证明):我不喜欢,因为它设计不好。
我们需要将接口 (IReminder,IRepository) 更改为covaraint。
在阅读IIdentifiableEntity数据时,我们需要转换为相应的concreate类类型。
示例代码如下所示:请遵循代码注释
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; }
}