C# 使用来自不同命名空间的两组“相同”类时,如何避免两次编写相同的方法

本文关键字:类时 何避免 方法 两次 两组 命名空间 相同 | 更新日期: 2023-09-27 18:31:57

例如,我有两个命名空间,每个命名空间中有三个类。就我而言,这两组三个类在名称和属性上是相同的,但每组都存在于自己的命名空间中。

正如您可以猜到的那样,此代码是从同一服务的两个实例(一个用于生产,另一个用于 UAT)自动生成的。每个实例可能在同一版本上,也可能不在同一个版本上,但是我关心的成员总是相同的。

例如:

namespace Domain.Uat //Auto-generated
{
    public class LoginResult { }
    public class ServiceSession { }
    public class Service
    {
        public ServiceSession Session { get; set; }
        public LoginResult Login(string username, string password);
    }
}
namespace Domain.Prod //Auto-generated
{
    public class LoginResult { }
    public class ServiceSession { }
    public class Service
    {
        public ServiceSession Session { get; set; }
        public LoginResult Login(string username, string password);
    }
}

我有一个类登录到该服务并执行一堆其他操作。我希望能够从UI层选择UAT或Prod,所以我在my(在本例中)Login()方法中添加了一个名为"isUat"的布尔参数。

我想避免编写两个几乎相同的 Login() 方法。 所以我在考虑这样的事情:

namespace Domain
{
    public class DomainClass
    {
        private object MyService; 
        private object MyLoginResult; 
        private object MySession;
        public void Login(string username, string password, bool isUat)
        {
            //New up the service here:
            if (isUat)
                MyService = new Domain.Uat.Service();
            else
                MyService = new Domain.Prod.Service();
            //Do all the common stuff here
            MyLoginResult = MyService.Login(username, password); 
            MySession = MyService.Session; 
        }
        //(Lots of other methods that use MyService and MySession go here)
    }
}

声明类型对象并强制转换为它是愚蠢的,但我认为它说明了我的一般方法。我已经让它以几种不同的方式使用部分类和接口("ILoginResult","IServiceSession","IService"等),但它感觉很黑客。

是否有解决此问题的最佳实践模式,或者我是否坚持编写两个 Login() 方法?

更新

我上面提到的分部类和接口方法的工作方式如下:

namespace Domain
{
    public interface ILoginResult { }
    public interface IServiceSession { }
    public interface IService
    {
        public IServiceSession Session { get; set; }
        public ILoginResult Login(string username, string password);
    }
}

然后我添加一些代码(不是自动生成的)来扩展自动生成的类:

namespace Domain.Uat //Do the same for Domain.Prod
{
    public partial class LoginResult : Domain.ILoginResult { }
    public partial class ServiceSession : Domain.IServiceSession { }
    public partial class Service : Domain.IService { } //Doesn't work, see below
}

然后我像这样修改我的域类:

namespace Domain
{
    public class DomainClass
    {
        private ILoginResult MyLoginResult;
        private IServiceSession MyServiceSession;
        private IService MyService;
        public void Login(string username, string password, bool isUat)
        {
            //New up the service here
            if (isUat)
                MyService = new Domain.Uat.Service();
            else
                MyService = new Domain.Prod.Service();
            //Common stuff here
            MyLoginResult = MyService.Login(username, password);
            MyServiceSession = MyService.ServiceSession;
        }
        //More members here
    }
}

但是,这不起作用,因为Domain.Uat.Service实际上并没有实现Domain.IService。 Domain.IService.ServiceSession 获取设置了一个 IServiceSession,但 Domain.Uat.Service 实际上没有一个名为 ServiceSession 的 IServiceSession 属性。登录结果也是如此。

所以,现在在我的部分类中,我不得不像这样实现新的具体成员:

namespace Domain.Uat //Do the same for Domain.Prod
{
    public partial class LoginResult : Domain.ILoginResult { }
    public partial class ServiceSession : Domain.IServiceSession { }
    public partial class Service : Domain.IService
    {
        public Domain.IServiceSession Domain.IService.ServiceSession
        {
            get { return Session; }
            set { Session = (Domain.Uat.ServiceSession)value; }
        }
        public Domain.ILoginResult Domain.IService.Login(string username, string password)
        {
            return Login(username, password);
        }
    }
}

我不喜欢这样,所以我尝试使用泛型来避免具体的实现,如下所示:

namespace Domain
{
    public interface ILoginResult { }
    public interface IServiceSession { }
    public interface IService<TLoginResult, TServiceSession>
        where TLoginResult : ILoginResult
        where TServiceSession : IServiceSession
    {
        public TServiceSession Session { get; set; }
        public TLoginResult Login(string username, string password);
    }
}

。这导致我的服务类签名发生变化,如下所示:

namespace Domain.Uat //Do the same for Domain.Prod
{
    public partial class LoginResult : Domain.ILoginResult { }
    public partial class ServiceSession : Domain.IServiceSession { }
    public partial class Service : Domain.IService<LoginResult, ServiceSession> { }
}

所以我避免了具体的实现,但现在当我在我的 DomainClass 中使用服务时编译器会中断:

namespace Domain
{
    public class DomainClass
    {
        private IService MyService; 
        private ILoginResult MyLoginResult; 
        private IServiceSession MySession;
        public void Login(string username, string password, bool isUat)
        {
            //New up the service here:
            if (isUat)
                MyService = new Domain.Uat.Service(); //Compiler fail, can't cast
            else
                MyService = new Domain.Prod.Service(); //Compiler fail, can't cast
            //Do all the common stuff here
            MyLoginResult = MyService.Login(username, password); 
            MySession = MyService.Session; 
        }
        //(Lots of other methods that use MyService and MySession go here)
    }
}

感觉我挖的洞太深了,所以我退后一步,在堆栈溢出上发帖!

附言协方差和逆变不适用于我的 IService 接口,因为一个成员是一个属性(是的,我需要能够在 Session 上获取和设置)。

C# 使用来自不同命名空间的两组“相同”类时,如何避免两次编写相同的方法

我不认为为每个类使用一个界面是笨拙的。 然后改成这样。

private ILoginResult MyLoginResult

然后为 domainclass 创建一个构造函数,并在构造函数中传递 uat 并实例化您的属性。

那么登录方法或任何其他方法就不需要 if 语句了

如果您无法控制生成的代码的接口,则可以为每个继承自公共接口/抽象类的服务创建一个包装器。

public class WrappedUatService : IService {
   private Domain.Uat.Service _service;
   public override ILoginResult Login() { 
       ILoginResult result = new WrappedLoginResult(_service.Login());
       ...
   }
}
public class WrappedProdService : IService {
   private Domain.Prod.Service _service;
   public override ILoginResult Login() { 
       ILoginResult result = new WrappedLoginResult(_service.Login());
       ...
   }
}

这是相当多的额外代码,但你不能仅仅因为它们碰巧具有具有相同签名的方法就将单独的类视为等效的类。