Возврат информации с пользовательскими исключениями C#


У меня есть следующая проблема:

  1. У меня есть метод обслуживания:

    public bool EmployeeCanReiceivePayment(int employeeId)
    {
       ...
    
       int workingHours = this.GetEmployeeWorkingHours(employeeId);
    
       if(workingHours < N)
       {
         throw new EmployeeCannotReceivePaymentException();
       }
    
       return true;
    }
    
     public int GetEmployeeWorkingHours(int employeeId)
    {
      //returns the number of working hours of the employee for the last month
    }
    
GetEmployeeWorkingHours-это всего лишь один из способов проверить, может ли работник получать зарплату (поэтому может быть и другая причина, по которой работодатель не платит). По каждой из этих причин я хочу создать исключение с соответствующей информацией: количество требуемых рабочих часов,количество фактических рабочих часов и т. д..

Вопрос в следующем:

Есть ли способ возвращает объект или дополнительную информацию с моим пользовательским исключением. А под дополнительной информацией я подразумеваю объект или просто несколько параметров.

2 2

2 ответа:

Избегайте использования исключений для потока управления

Это правило использования .NET Framework

DA0007: избегайте использования исключений для потока управления

Использование обработчика исключений в рамках регулярного выполнения программы логика может быть дорогостоящей, и ее следует избегать. В большинстве случаев, исключения должны использоваться только для обстоятельств, которые происходят нечасто и не ожидаются..

Вместо создания исключения создайте класс которая будет содержать всю необходимую информацию.

public class PaymentValidation
{
    public bool IsValid { get; set;}

    public YourType SomeAdditionalInformation { get; set;}
}

public PaymentValidation EmployeeCanReiceivePayment(int employeeId)
{
    int workingHours = this.GetEmployeeWorkingHours(employeeId);

    var validationResult = new PaymentValidation();
    validationResult.IsValid = true;

    if(workingHours < N)
    {
        validationResult.IsValid = false;
    }

    return validationResult;
}

Возвращение собственного типа даст больше возможностей для дальнейших изменений.

Например, создайте интерфейс, который представляет возвращаемый тип. И создайте собственную реализацию для каждого случая, который у вас есть

public interface IPayment
{
    void Pay();
}

Реализовать интерфейсы для каждого случая

public class NotEnoughWorkingHoursPayment : IPayment
{
    public void Pay()
    {
        //Do nothing or log failed payment or inform user about it
    }
}

public class SuccesfullPayment : IPayment
{
    public void Pay()
    {
        //Execute payment
    }
}


public class PaymentService
{
    public IPayment ValidatePayment()
    {
        const int MIN_WORKING_HOURS = 40;
        int workingHours = this.GetEmployeeWorkingHours(employeeId);

        if(workingHourse < MIN_WORKING_HOURS)
        {
            return New NotEnoughWorkingHoursPayment();
        }

        return new SuccesfullPayment();
    }
}

Тогда использование будет очень простым и понятным

IPayment payment = paymentService.ValidatePayment();

payment.Pay();

Я согласен с тем, что другие говорят здесь, что принуждение вашего BL исключениями не является хорошей практикой. Вместо исключения вы можете вернуть "ResultObject", который содержит статус успеха + сведения и допустимые возвращаемые данные.

Но если вы решите сделать это, то настройте свое пользовательское исключение, чтобы включить в него не только конструктор по умолчанию. Они такие же, как и любой другой класс. Что-то вроде:

public class MyCustomException : Exception
{
   //This is like you have now
   public MyCustomException() { }

   //This is if you want to have different messages
   public MyCustomException(string message)  : base(message{ }

   //This is if you want to have different data to add (don't use object but a custom type
   public MyCustomException(object yourObject)
   {
      YourObject = yourObject;
   }

   public object YourObject { get; set; }
}

В вашем сценарии тогда, вероятно, что-то вроде:

public class EmployeeException : Exception { ... }
//Add what you need from the example above

//Then when needed:
new EmployeeException("some text for this error");
new EmployeeException("some other text for this error");

//Or when with a proper object to describe more details:
new EmployeeException(new NotEnoughHours { ... });