为类方法定义临时对象

时间:2018-12-24 13:59:47

标签: c# class

我有一个名为“ HotelUtilities”的公共类,其中包含许多方法。实现可在HotelUtilities方法之间共享的TEMPORARY对象的最佳方法是什么? (我不希望在HotelUtilities主类之外访问该对象。)最好的方法是为游客使用结构,其他类或其他东西吗?例如,看一下TEMPORARY对象“ Visitor” ...

namespace Utilities
{
    public static class HotelUtilities
    {
        public class TempVisitor
        {
            internal Guid UserId;
            internal string EmailAddress;
            internal string FirstName;
            internal string LastName;
            internal integer CountryCode;
        }

        public static List<TempVisitor> GetForeignVisitors()
        {

Visitor对象只是TEMPORARY,并且只能由HotelUtilities类中的各种方法使用/共享。我不希望在HotelUtilities之外使用它。

谢谢!

1 个答案:

答案 0 :(得分:1)

要回答您的直接问题,您可以定义在公共方法中使用的空类或接口。有一个私有类,可以使用您想要的属性/字段来扩展空类,并将其强制转换。

public static class HotelUtilities
{
    public interface ITempVisitor
    {
    }

    private class HiddenTempVisitor : ITempVisitor
    {
       internal Guid UserId;
    }

    public static List<ITempVisitor> GetTempVisitors()
    {
        return new List<ITempVisitor>() { new HiddenTempVisitor { UserId = Guid.NewGuid } };
    }

    public static void UseTempVisitors(List<ITempVisitor> visitors)
    {
        foreach (HiddenTempVisitor visitor in visitors)
        {
            Console.WriteLine(visitor.UserId);
        }
    }
}

但是,对我来说,这似乎确实是一种代码气味。实际上,我认为这很可怕。考虑如果其他人创建了自己的扩展ITempVisitor的类,则UseTempVisitors将因无效的强制转换异常而崩溃。

另一种可能性是将返回类型更改为object,并且在需要使用它时,该方法接受一个对象并将其强制转换为List。这样,TempVisitor是否为私有并不重要,因为它不会成为任何公共接口的一部分。但这仍然很糟糕,因为当传入List以外的数据类型时,编译器不会给出错误。

由于您尝试做的事情与OO设计不太吻合,这可能意味着您可以重新设计代码,使其工作更加自然。在非静态类中,您可以将TempVisitor类设为私有,并将TempVisitors列表保留在私有字段中。为了使代码更易于理解,您可能希望将使用TempVisitor的方法提取到名称更有意义的新类中,但是有些人认为实用程序/帮助程序类是反模式,因此这样做可能会很好无论如何。

public class EventGuestEmailer
{
    private class GuestInfo
    {
        public Guid GuestId;
        public string EmailAddress;
    }

    private List<GuestInfo> _guests;
    private IDataAccess _dataStore;
    private IEmailSender _emailer;

   public EventGuestEmailer(IDataAccess dataStore, IEmailSender emailer)
   {
       _dataStore = dataStore;
       _emailer = emailer;
   }

   public void GetGuestsAtEvent(int eventId)
   {
       if (_guests != null) throw new InvalidOperationException($"Cannot call {nameof(GetGuestsAtEvent)} more than once");

       _guests = new List<GuestInfo>();
       foreach (var result in _dataStore.GetEventAttendees(eventId))
       {
           if (result.IsGuest)
           {
             _guests.Add(new GuestInfo { GuestId = restult.GuestId, EmailAddress = result.EmailAddress });
           }
       }
   }

   public SendEmailToGuests(ITemplate emailTemplate)
   {
       if (_guests == null) throw new Exception($"{nameof(GetGuestsAtEvent)} must be called before {nameof(SendEmailToGuests)}");

       foreach (var guest in _guests)
       {
           var emailBody = template.Apply(guest.GuestId);
           _emailer.Send(emailBody, guest.EmailAddress);
       }
   }
}

这有几个优点。类名描述了它的作用,而HotelUtilities没有描述,这使单一职责类更易于理解。它是可测试的,使用模拟框架创建其使用的接口的模拟,并且您可以轻松测试类的业务逻辑,包括边缘情况和错误情况。它封装了业务逻辑单元,因此,如果在事件发生后对向谁发送电子邮件的需求发生变化,则由于使用了一个有用的名称,因此很容易找到该类,并且代码位于一个位置,而不是将访问者列表放在一个位置,从另一个模板构建电子邮件正文,然后从第三位发送电子邮件。

基本上,您的问题就像是XY problem的情况。您正在做某事,遇到一个问题,您正在寻找解决方法。如果退后一步,寻找其他方法,可能会找到根本不需要解决方法的解决方案。