接口问题 C# 和依赖项注入



我遇到了问题。让我们看一个例子: 你得到了这个接口,它将由 Employee.cs 和 Owener.cs 实现:

public interface IEmployee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
}
public class Employee: IEmployee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
}
public class Owner: IEmployee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
public string Status{ get; set; } <--- //problem string
}

现在,当我们使用依赖注入并且它返回员工或经理的对象时,这就是我遇到问题的地方。

public class EmployeeCheck{
private IEmployee empObj;
public EmployeeCheck(IEmployee _em)
{
empObj=_em
}
public void PrintCheck()
{
string str=_em.FirstName;
string str2=(Owner)_emp.Status <--- //problem...how do I access it?? It can't be accessed cause 
//IEMployee doesn't have status field!
}

所以基本上如果我使用 IEmployee 作为接口,我无法访问新的 Owner 类中的字段,如果我将它们放在接口中,那么不需要实现它的 Employee 类将被迫实现它不需要的东西!由于DI注入或其他设计模式,我确实需要IEmployee。


好吧,我不能使用抽象类...因此,让我们讨论更多关于IStatus解决方案的信息...所以你说的是这样的代码:

public interface IStatus:IEmployee
{
public string Title { get; set; }
}

公共类 所有者:IEmployee,IStatus { 公共字符串 FirstName { get; set; }

public string LastName { get; set; }
public string Location { get; set; }
public string Status{ get; set; } <--- //problem string

}

但是我如何在员工检查类中工作呢?

公共类员工检查 {

private IEmployee empObj;
public EmployeeCheck(IEmployee _em, IStatus)
{
empObj=_em
}

}

您正在处理的场景可以在传统上使用StructureMap或Unity等IoC Containers来处理,方法是使用称为命名实例的东西。这些容器提供开箱即用的此类功能。

同样可以通过多种方式在.NET Core中实现。一种方法是使用IServiceCollection中的扩展方法。下面的代码片段将引导你了解如何在你的方案中完成此操作

// using Microsoft.Extensions.DependencyInjection
// Startup.cs - ConfigureServices()
services.AddTransient(serviceProvider =>
{
Func<string, IMyClass> func = key =>
{
switch (key)
{
case "MyClass":
return serviceProvider.GetService<MyClass>();
case "MyClass1":
return serviceProvider.GetService<MyClass2>();
default:
throw new KeyNotFoundException();
}
};
return func;
});
//Register your services here as usual
services.AddTransient<IMyClass, MyClass>();
services.AddTransient<IMyClass, MyClass2>();

您实际上是在这里创建一个工厂,该工厂将根据key给出所需类型的依赖项。以下代码片段演示如何在控制器中完成此操作。

// ctor of your controller
public MyController(Func<string, IMyClass> injector)
{
// key here could be 'MyClass' or 'MyClass2'
IMyClass service = injector("<key>");
}

以下是我为上述示例考虑的示例类的结构

// implementation 1
public class MyClass : IMyClass
{
}
// implementation 2
public class MyClass2 : IMyClass
{
}
// interface
public interface IMyClass
{
}

还有其他方法可以处理此问题。您可以查看此答案以获取其他方法。

这取决于你如何使用或为什么你必须使用依赖注入。我认为在这些情况下,根据您的示例,使用它不是那么好,因为它在简单的事情上给您带来了复杂性。

如果要使用 Status 值执行操作,可以通过生成一个新接口来隔离接口。喜欢这个。public interface IStatus { string Status { get; set; } },然后仅在所有者和构造函数中实现此接口 EmployeeCheck 注入IStatus.

但是,如果没有必要,为什么不IEmployee,您将其作为抽象类进行。

public abstract class Employee
{
public string Name { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
}
public class Owner : Employee
{
public string Status { get; set; }
}
public class EmployeeCheck 
{
public EmployeeCheck(Employee employee)
{
var owner = employee as Owner;
var statuts= owner.Status;
}
}

您需要考虑的第一个问题是:当它用Employee(比如)实例化时,EmployeeCheck应该做什么,因为它似乎需要Status来打印支票?

接口背后的整个思想是,它们为可以使用对象执行的操作提供协定。 在这种情况下,您正在尝试执行协定中未指定的事情(使用Status),因此类型系统使其变得有点困难(迫使您强制转换)。

避免强制转换的一个选项(如@Diegorincon所建议的)是创建另一个实现IEmployee的接口(类似于IHasStatus),然后将EmployeeCheck中的类型更改为IEmployeeWithStatus(CheckPrinter可能更清晰?

interface IEmployee
{
string FirstName { get; }
string LastName { get; }

}
interface IEmployeeWithStatus:IEmployee
{
string Status { get; }
}
public class Owner : IEmployeeWithStatus
{
public string FirstName { get; }
public string LastName { get; }
public string Status { get; }
}
class EmployeeCheck
{
private readonly IEmployeeWithStatus _employeeWithStatus;
public EmployeeCheck(IEmployeeWithStatus employeeWithStatus)
{
_employeeWithStatus = employeeWithStatus;
}
void PrintCheck()
{
// no casting needed
Console.Write($"{_employeeWithStatus.FirstName} {_employeeWithStatus.LastName} {_employeeWithStatus.Status}");
}
}

如果您坚持使用示例中的签名,则编写代码的一个选项是使用以下类型模式编写 switch 语句:

switch (employee)
{
case Owner owner:
{
// you can use owner.Status here
Console.WriteLine(owner.Status);
}
break;
case Employee employee:
{
// hmm.., now what?!
}
break;
}

这比到处投掷要干净,但归根结底,它只是在推动同样的问题。

(在面向对象范式中,这种情况一直出现,当您手头有一个更通用的类型(如Animal)的对象,但您发现自己想要基于其特定类型(如Dog)进行操作。 像上面的代码一样打开类型通常被认为是代码异味。 有了更多细节,我也许可以提供一些其他的想法)

最新更新