Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - Passing different type of objects through the same method

Tags:

c#

.net

Original Question

So I have this 3 objects...

public class obj1
{
  public int Id { get; set; }
  public string Name { get; set; }
}

public class obj2
{
  public int AccNum { get; set; }
  public string Name { get; set; }
}

public class obj3
{
  public string Email { get; set; }
  public string Phone { get; set; }
}

... and one method that is supposed to receive one of them, after evaluating the object type the program should decide which function to call.

I've tried with generics but it doesn't work as I expected. So far this is what I've got...

    public class NotificationHelper: INotificationHelper
    {

        public bool SendNotification<TNotInfo>(TNotInfo obj) where TNotInfo : class
        {
            if (contract.GetType() == typeof (obj1))
            {
                var sender = new SendSMS();
                return sender.Send(obj);
            }
            if (contract.GetType() == typeof(obj2))
            {
                var sender = new SendPush();
                return sender.Send(obj);
            }
            else
            {
                var sender = new SendEmail();
                return sender.Send(obj);
            }
        }
    }

but I get the error "Cannot convert from TNotInfo to Models.obj1". Is there any way to overcome this issue? Or I have to change my logic?

Appreciate any help, thanks in advance.

*Edit

using System;

namespace EmailNotifications
{

    public interface IEmailNotification
    {
        void SendEmailNotification();
    }

    public class EmailNotificationA : IEmailNotification
    {

        public void SendEmailNotification(Contract1 a)
        {
            Console.WriteLine($"Sending EmailNotificationA ({a})");
        }
    }

    public class EmailNotificationB : IEmailNotification
    {
        public void SendEmailNotification(Contract2 b)
        {
            Console.WriteLine($"Sending EmailNotificationB ({b})");
        }
    }

    public class EmailNotificationC : IEmailNotification
    {

        public void SendEmailNotification(Contrac3 c)
        {
            Console.WriteLine($"Sending EmailNotificationC ({c})");
        }
    }

    public class EmailNotificationService
    {
        private readonly IEmailNotification _emailNotification;

        public EmailNotificationService(IEmailNotification emailNotification)
        {
            this._emailNotification = emailNotification;
        }

        public void ServiceHelper()
        {
            _emailNotification.SendEmailNotification();
        }
    }
}

Above solution is what I was trying to achieve, applying strategy design pattern. But I couldn't manage to make my interface method receive different objects, this is required because each notification has is own implementation. As visible at the none working example above, I have 3 different implementation of the same method all of them receiving different objects. Any idea of how to make this logic work?

like image 439
iniestar Avatar asked Nov 29 '22 22:11

iniestar


1 Answers

This is the kind of thing that interfaces were designed to do. First, define a common interface:

public interface INotifier
{
    bool Notify();
}

Second, implement it in your objX classes:

public class obj1 : INotifier
{
    public int Id { get; set; }
    public string Name { get; set; }

    public bool Notify()
    {
        var sender = new SendSMS();
        return sender.Send(this);
    }
}

public class obj2 : INotifier
{
    public int AccNum { get; set; }
    public string Name { get; set; }

    public bool Notify()
    {
        var sender = new SendPush();
        return sender.Send(this);
    }
}

public class obj3 : INotifier
{
    public string Email { get; set; }
    public string Phone { get; set; }

    public bool Notify()
    {
        var sender = new SendEmail();
        return sender.Send(this);
    }
}

And finally, change your notification method to accept the interface type as the parameter:

public class NotificationHelper : INotificationHelper
{
    public bool SendNotification(INotifier obj)
    {
        return obj.Notify();
    }
}

Edit (2019):

I'm revisiting this answer as it seems to be getting a fair amount of visibility. OP has probably long since moved on, but for others that may stumble upon this answer, here's another solution.

I still believe that interfaces are the way to go. However, the interface suggested above is extremely generic and ultimately not terribly useful. It also runs into some DRY violations because, as Fabio said in a comment, if two objX classes implement notifications in the same way, this approach forces you to duplicate the code between them.

Instead of one global interface, instead have interfaces for each specific notification task, i.e. ISMSNotification, IPushNotification, IEmailNotification. You can then use the mixin pattern to give each interface instance a default implementation of the send method:

interface ISmsNotifier
{
  int SmsId { get; }
  string SmsName { get; }
}

static class ISmsNotifierExtensions 
{
  public static bool NotifySms(this ISmsNotifier obj) 
  {
    var sender = new SendSMS();
    return sender.Send(obj);
  }
}

// ---------------------------------------------

interface IPushNotifier 
{
  int PushAccNum { get; }
  string PushName { get; }
}

static class IPushNotifierExtensions 
{
  public static bool NotifyPush(this IPushNotifier obj) 
  {
    var sender = new SendEmail();
    return sender.Send(obj);
  }
}

// ---------------------------------------------

interface IEmailNotifier 
{
  string EmailAddress { get; }
  string EmailPhone { get; }
}

static class IEmailNotifierExtensions 
{
  public static bool NotifyEmail(this IEmailNotifier obj) 
  {
    var sender = new SendEmail();
    return sender.Send(obj);
  }
}

You can then implement it in the objX classes like so:

public class obj1 : INotifier, ISmsNotifier 
{
  public int SmsId { get; set; }
  public string SmsName { get; set; }

  public bool Notify() => this.NotifySms();
}

public class obj2 : INotifier, IPushNotifier
{
    public int PushAccNum { get; set; }
    public string PushName { get; set; }

    public bool Notify() => this.NotifyPush();
} 

public class obj3 : INotifier, IEmailNotifier
{
    public string EmailAddress { get; set; }
    public string EmailPhone { get; set; }

    public bool Notify() => this.NotifyEmail();
}

Notice that using this approach it's easy to not only support objects which use identical notification systems, you can also support objects with multiple notification systems:

public class obj4 : INotifier, IEmailNotifier, IPushNotifier
{
    public int PushAccNum { get; set; }
    public string PushName { get; set; }
    public string EmailAddress { get; set; }
    public string EmailPhone { get; set; }

    public bool Notify() => this.NotifyEmail() && this.NotifyPush();
}

You might notice that this approach makes NotificationHelper obsolete since it's no longer necessary to pass the objects through a processing step to determine which notification system to process the object through. That is true, and maybe rightfully so, since the objects should be fully capable of deciding that for themselves (depending on your mentality approaching this problem). However, NotificationHelper may still have its uses, such as if you wanted to preprocess the information that's getting sent to the notification services, or if you wanted a common point of entry to help with mocking and testing.

C# 8 Note:

A proposed feature of C# 8 is the ability to give interfaces a default implementation of methods within the interface definition itself. When (if) that happens, you don't need to use the mixin pattern anymore and can directly define the default method implementations in the interfaces. The feature hasn't yet been finalized, but it might look something like this:

interface ISmsNotifier 
{
  int SmsId { get; }
  string SmsName { get; }

  public bool NotifySms() 
  {
    var sender = new SendSMS();
    return sender.Send(this);
  }
}

// ---------------------------------------------

interface IPushNotifier 
{
  int PushAccNum { get; }
  string PushName { get; }

  public bool NotifyPush() 
  {
    var sender = new SendEmail();
    return sender.Send(this);
  }
}

// ---------------------------------------------

interface IEmailNotifier 
{
  string EmailAddress { get; }
  string EmailPhone { get; }

  public bool NotifyEmail() 
  {
    var sender = new SendEmail();
    return sender.Send(this);
  }
}
like image 173
Abion47 Avatar answered Dec 15 '22 03:12

Abion47