在 C# 中,一个类可以返回两种不同类型的对象,需要遍历它们



我对C#并不陌生,但我并没有真正使用泛型和DI,但我遇到了一个自学的项目,所以我借此机会。这是场景(会很长,我感谢您的耐心等待(...

我们有一个托管 Web 服务的本地解决方案。我有一个应用程序,它将访问这些网络服务,为我们的合作伙伴之一提取信息并生成数据文件。 我们正在从本地解决方案转向基于云的解决方案(相同的供应商,相同的Web服务,云中的实现略有不同(。 我决定最好通过执行以下操作来学习接口(比我现在知道的更多(、泛型(我在键入此内容时正在学习(和依赖注入(也是该特定领域的初学者(

为我正在调用的每个 Web 服务创建一个接口,即具有函数 CallUserService 返回列表的 IGetUsers 为云解决方案和本地解决方案分别实现一次接口

public class GetUsersCloud : IGetUsers { public List<string> CallUserService(int ID)... }
public class GetUsersPrems : IGetUsers { public List<string> CallUserService(int ID)... }

现在我有一个带有构造函数的类,该构造函数采用 IGetUsers 的实例

public class GetUsers {        
private IGetUsers _getUsers;
public GetUsers(IGetUsers getUsers) {
_getUsers = getUsers;
}

从这里,我可以使用处理本地解决方案或云解决方案的类的实例实例化此类,并调用

GetUsers getUsers;
if (!cloud) {
getUsers = new GetUsers(new GetUsersPrems());
} else {
getUsers = new GetUsers(new GetUsersCloud());
}
List<string> userList = getUsers.CallUserService(5);

这很好用,因为我的返回类型是列表,所以我可以很容易地处理这个问题。这就是我稍微卡住的地方。

我有另一个接口要实现,但是将实现它的类将返回不同类型的 Web 服务。我的意思是,虽然这两个 Web 服务将返回一个名为 UserDetailList 的类型,但它们来自两个不同的强类型类,这些类是通过从本地和云解决方案导入服务引用而创建的。为了解决这个问题,我创建了一个接口,其中包含一个返回泛型的函数

public interface IUserDetails {
T GetUserDetails<T>(int ID);
}

我的类实现看起来像这样(显然缺少肉和土豆,但你明白了(

public class GetUserDetailsCloud : IUserDetails {
public T FetchUserDetails<T>(int ID) { 
CloudWS.UserDetailList userDetailList = cloudWebservice.GetUserDetailList(ID);
return (T)(object)userDetailList;
}
}
public class GetUserDetailsPrems : IUserDetails {
public T FetchUserDetails<T>(int ID) { 
PremsWS.UserDetailList userDetailList = premsWebservice.GetUserDetailList(ID);
return (T)(object)userDetailList;
}
}

我创建了一个带有构造函数的类,该构造函数采用 IUserDetails 的实例

public class GetUserDetails{        
private IUserDetails _getUserDetails;
public GetUserDetails(IUserDetails getUserDetails) {
_getUserDetails= getUserDetails;            
}
}

现在很容易,我可以根据我是否在云上进行实例化:

GetUserDetails getUserDetails;     
object userDetailsResults;
if (!cloud) {
getUserDetails = new GetUserDetails (new GetUserDetailsPrems());
userDetailsResults = getUserDetails.FetchUserDetails<PremsWS.UserDetailList>(ID);  
} else {
getUserDetails = new GetUserDetails (new GetUserDetailsCloud());
userDetailsResults = getUserDetails.FetchUserDetails<CloudWS.UserDetailList>(ID);
}

这就是我卡住的地方。由于这两个 Web 服务具有不同的类型,因此我调用它并根据我是否是云来传递类型,然后我的结果存储在对象中,而不是强类型类的实例。我的后续步骤包括循环浏览结果并执行操作/填充输出文件。我曾经可以说,用一种类型...

foreach (PremsWS.UserDetail in userDetailsResults) {
<do some trivial stuff here>
}

现在在我看来,我必须编写两个单独的函数来循环数据并处理结果。我觉得应该有一种方法让我编写一个函数,该函数采用泛型类型,然后根据它是什么(无论是云实例还是本地实例(,处理结果。

我一直坐在这里试图弄清楚,我的谷歌让我找不到教程或任何可以指出我正确方向的东西,我将不胜感激。

这里理想的路径是从GetUserDetailList返回相同的类型。由于这似乎不是一种选择,因此将两种返回类型视为同名在实现它们的分离方面造成了一些伤害。

他们也可能返回类型WebLocal.此外,您似乎无法更改UserDetailList类型。

因此,您唯一可以引入修复的地方是在消费点。拥有强类型实例后,需要对其进行抽象。由于您至少知道属性,因此最好的方法是使用复制构造函数来促进抽象。

public class LocalUserDetailList
{
public [[ underlying name and type for the UserList data ]] { get; set; }
public LocalUserDetailList(PremsWS.UserDetailList UserDetailList)
{
// populate the publicly available list above^
}
public LocalUserDetailList(CloudWS.UserDetailList UserDetailList)
{
// populate the publicly available list above^
}
}

现在,您可以使用此复制构造函数来返回要使用的统一类型。

public interface IUserDetails 
{
LocalUserDetailList FetchUserDetails(int ID);
}
public class GetUserDetailsCloud : IUserDetails {
public LocalUserDetailList FetchUserDetails(int ID) { 
CloudWS.UserDetailList userDetailList = cloudWebservice.GetUserDetailList(ID);
return new LocalUserDetailList(userDetailList);
}
}
public class GetUserDetailsPrems : IUserDetails {
public LocalUserDetailList FetchUserDetails(int ID) { 
PremsWS.UserDetailList userDetailList = premsWebservice.GetUserDetailList(ID);
return new LocalUserDetailList(userDetailList);
}
}

最后访问统一实例,无需object通话。

GetUserDetails getUserDetails;     
LocalUserDetailList userDetailsResults;
if (!cloud) {
getUserDetails = new GetUserDetails (new GetUserDetailsPrems());
userDetailsResults = getUserDetails.FetchUserDetails(ID);  
} else {
getUserDetails = new GetUserDetails (new GetUserDetailsCloud());
userDetailsResults = getUserDetails.FetchUserDetails(ID);
}

虽然这不使用泛型,但它确实有效,并且易于阅读和调试。有时,泛型可能会变得草率,在这种情况下会如此。如果您继续使用 object 作为类型,则需要使用属性迭代才能在迭代期间获取值。这并不理想,速度很慢,并且可能导致代码非常难以阅读。

此外,使用这样的抽象还应该允许您在代码中实现可以删除重复调用并只返回一个复制构造函数实例的位置。如果你要利用你的抽象版本和泛型,那将是一种更强大和集成的方式来使用该工具。

最新更新