Multiple classes that implement a generic interface in a single array - c#

Say I have a generic interface that stores a value that is typed by the generic parameter:
public interface IFoo<TValue>
{
TValue SomeValue { get; set; }
}
Then I have two classes, StringFoo and DoubleFoo whose SomeValues are strings and doubles respectively:
public class StringFoo : IFoo<string>
{
string SomeValue { get; set; }
}
public class DoubleFoo : IFoo<double>
{
double SomeValue { get; set; }
}
I now decide that I want an array that can contain both StringFoos and DoubleFoos:
var foos = IFoo<object>[] {
new StringFoo { SomeValue = "a value" },
new DoubleFoo { SomeValue = 123456789 }
}
I would think that, since both string and double are subclasses of object, they would both be allowed in this array. However, I thought wrong.
So, I tried using covariance:
public interface IFoo<out TValue>
But, since the interface contains both setters and getters, I can't do this.
So, can two classes that implement a generic interface in one array?

The problem can be solved in the way Bar class is implemented (bad example provided as well). The issue is that whenever one tries to use generic interface or class implementing generic interface code may compile (providing right casts) but code will throw InvalidCastException during runtime.
public interface IFoo<TValue>
{
TValue SomeValue { get; set; }
}
public class StringFoo : IFoo<string>
{
public string SomeValue { get; set; }
}
public class DoubleFoo : IFoo<double>
{
public double SomeValue { get; set; }
}
public class Foo<TValue> : IFoo<TValue>
{
public TValue SomeValue { get; set; }
}
public abstract class Bar
{
}
public class Bar<TValue> : Bar, IFoo<TValue>
{
public TValue SomeValue { get; set; }
}
public static class Verify
{
public static void QuestionArray()
{
var foos = new IFoo<object>[]
{
(IFoo<object>) new StringFoo { SomeValue = "a value" },
(IFoo<object>) new DoubleFoo { SomeValue = 123456789 }
};
}
public static void BadAnswerArray()
{
var foo = new IFoo<object>[]
{
(IFoo<object>) new Foo<string>(),
new Foo<object>(),
};
}
public static void GoodAnswer()
{
var foo = new Bar[]
{
new Bar<string>(),
new Bar<object>(),
new Bar<double>()
};
}
}
And to verify solution one can run tests where only GoodAnswerTest will pass:
public class GenericsInArrayTests
{
[Fact]
public void QuestionArrayTest()
{
Verify.QuestionArray();
}
[Fact]
public void BadAnswerTest()
{
Verify.BadAnswerArray();
}
[Fact]
public void GoodAnswerTest()
{
Verify.GoodAnswer();
}
}

Related

How to use Generic Delegate to access Derived Class Members?

Let me preface by saying I am new to coding and C# is my first language
I have a interface:
public interface ITDXInput <TSCContext, TContext, TWaferContainer> : ITDXInput
{
TSCContext SCContext { get; set; }
TContext ToolContext { get; set; }
TWaferContainer WaferContainer { get; set; }
}
This class implements the interface:
public class CIMTDXInput : ITDXInput<CIMSCContext, CIMToolContext, CIMWaferContainer>
{
public CIMWaferContainer WaferContainer { get; set; }
public CIMSCContext SCContext { get; set; }
public CIMToolContext ToolContext { get; set; }
}
The members of this class inherit from the following interfaces:
public class CIMSCContext : ISCContext
{
public string PROCESSING_END_TIME { get; set; }
public string PRODUCT_NAME { get; set; }
public string QUANTITY_IN { get; set; }
public string LOT_OWNER { get; set; }
public string FW_VERSION { get; set; }
}
public class CIMToolContext : IToolContext
{
public string LOT { get; set; }
public string TDX_MULTI_FILES { get; set; }
public string PROCESSING_START_TIME { get; set; }
public string WITHIN_UNIT_AXIS_DIRECTION { get; set; }
}
public class CIMWaferContainer : IWaferContainer
{
public CIMWaferContext WaferContext = new CIMWaferContext();
public List<CIMWaferImage> ImageList = new List<CIMWaferImage>();
public List<CIMWaferDieResults> WaferResultList = new List<CIMWaferDieResults>();
}
I have a delegate handling class:
public class KlarfTemplateDelegateHandler
{
public static Dictionary<string, Delegate> HandlerMap = new Dictionary<string, Delegate>();
public static Dictionary<string, Delegate> DefectListHandlerMap = new Dictionary<string, Delegate>();
delegate string TemplateDelegate<T,U,V>(ITDXInput<T, U, V> cimInput);
delegate string DefectListTemplateDelegate(CIMTDXInput cimInput, List<CIMKlarfDefectList> list);
static KlarfTemplateDelegateHandler()
{
HandlerMap["#DATA.TOOL_CONTEXT.PROCESSING_END_TIME_DATE#"] = new TemplateDelegate<ISCContext,IToolContext, IWaferContainer>(ProcessEndDate);
HandlerMap["#DATA.TOOL_CONTEXT.PROCESSING_END_TIME_TIME#"] = new TemplateDelegate(ProcessEndTime);
}
private static string ProcessEndDate<T,U,V>(ITDXInput<T,U,V> cimInput)
{
DateTime dateTime = DateTime.Parse(cimInput.ToolContext.PROCESSING_END_TIME);
//return cimInput.CIMSCContext.PROCESSING_END_TIME.Split(' ')[0];
return dateTime.ToString("MM-dd-yyyy");
}
}
The issue is that for the function ProcessEndDate, it tells me that 'T' does not contain a definition for "PROCESSING_END_TIME_TIME". I tried to replace TSCContext inside ITDXInput with ISCContext as the generic for the ITDXInput's TSCContext but it's localized.
Is there anything I can do to be able to get the function to see that whatever ITDXInput I pass into the ProcessEndDate function will have the PROCESSING_END_TIME_TIME definition?
I am going through this exercise because I would like to reuse that delegatehandler class for other types of TDXInput
One approach would be to constrain the ProcessEndDate method. A minimal example of this would be to have this interface:
public interface ISCContext
{
string PROCESSING_END_TIME { get; set; }
}
with two different classes that implement it:
public class ClassA : ISCContext
{
public string PROCESSING_END_TIME { get; set; } = "A:" + DateTime.UtcNow.ToString();
}
public class ClassB : ISCContext
{
public string PROCESSING_END_TIME { get; set; } = "B:" + DateTime.UtcNow.ToString();
}
To let the compiler know what to expect in a generic method, you would use the keyword where to make an interface constraint. For clarity, I made a new method with only 1 generic argument. You could, of course, have multiple arguments and corresponding constraints to define expectations for T, U and V but this gives you the basic idea.
private static void showProcessingEndTime<T>(T item) where T : ISCContext
{
Console.WriteLine(item.PROCESSING_END_TIME);
}
Here's a Microsoft article explaining the many ways Generic Type Constraints may be used.
Test code
static void Main(string[] args)
{
var a = new ClassA();
showProcessingEndTime(a);
var b = new ClassB();
showProcessingEndTime(b);
}

Refactoring classes into multi-layered generic classes in C#

I have a problem with C# generics, and I'm not sure about the most elegant solution. I've been programming a while but am new to the C# ecosystem so don't know common terminology for searching.
I'm trying to refactor code to reduce existing copy-paste duplication of classes. It is easy to resolve with one level of generics, but I can't get it working with two.
A very simplified example is below. The core issue is that BaseProfile cannot use any implementation details relating to DetailsA or DetailsB as it does not know the type. So UpdateDetailsId() has to be duplicated in 2 derived classes, instead of having a single Profile class handle it. Keep in mind this is a toy example just to express the relationships. The real classes have tens of fields, but a common subset which we are using in the class in question, so even if DetailsA and DetailsB look identical assume we need both.
public abstract class BaseProfile<TypeOfPerson>
{
public TypeOfPerson Person { get; set; }
}
public class Profile1 : BaseProfile<PersonA>
{
public void UpdateDetailsId(int id)
{
this.Person.Details.Id = id;
}
}
public class Profile2 : BaseProfile<PersonB>
{
public void UpdateDetailsId(int id)
{
this.Person.Details.Id = id;
}
}
public class PersonA
{
public DetailsA Details { get; set; }
}
public class PersonB
{
public DetailsB Details { get; set; }
}
public class DetailsA
{
public int Id { get; set; }
}
public class DetailsB
{
public int Id { get; set; }
}
I can add interfaces as it is referring to all the same fields for each type. However, C# will not allow an interface to include another interface and automatically resolve it in the implementation, because the member has to exactly match i.e. I thought I could just add IDetails Details to the IPerson interface but the fields now need to be type IDetails instead of DetailsA which implements IDetails. If I do that then I lose compiler type safety and can put the wrong Details on the wrong Person.
I have had success doing a public/private field pair like below, but this only validates and throws at runtime when casting value to DetailsA. I'd prefer something safer but I don't know if this is the best option. The goal of this example is a single Profile class, handling multiple Person classes, each with their own Details type that has an int Id field.
public class PersonA : IPerson
{
public IDetails Details
{
get { return _details; }
set { _details = (DetailsA)value; }
}
private DetailsA _details { get; set; }
}
One way of achieving this is by defining the type relationship between PersonA to DetailsA in a generic way, and specify a second generic type on BaseProfile.
Profile1 : BaseProfile<PersonA, DetailsA>
Consider the following code (note that I'm using Net6, so I have all these nullable reference type operators):
public abstract class BaseProfile<TPerson, TDetails>
where TDetails : IDetails, new()
where TPerson : PersonDetails<TDetails>, new()
{
public TPerson? Person { get; set; } = new TPerson();
public virtual void UpdateDetailsId(int id)
{
Person!.Details!.Id = id;
}
}
public class Profile1 : BaseProfile<PersonA, DetailsA>
{
}
public class Profile2 : BaseProfile<PersonB, DetailsB>
{
}
public abstract class PersonDetails<TDetails>
where TDetails : IDetails, new()
{
public virtual TDetails? Details { get; set; } = new TDetails();
}
public class PersonA : PersonDetails<DetailsA>
{
}
public class PersonB : PersonDetails<DetailsB>
{
}
public interface IDetails
{
int Id { get; set; }
}
public class DetailsA : IDetails
{
public int Id { get; set; }
public string? FirstName { get; set; }
}
public class DetailsB : IDetails
{
public int Id { get; set; }
public string? LastName { get; set; }
}
Testing with the following snippet
var profile1 = new Profile1();
var profile2 = new Profile2();
profile1.UpdateDetailsId(10);
profile2.UpdateDetailsId(12);
Console.WriteLine(profile1.Person!.Details!.Id);
Console.WriteLine(profile2.Person!.Details!.Id);
Console.WriteLine();
Update:
Because you included explicit casting in your snippet for Details property getters and setter, I also want to show a pattern using a concrete type inheriting on these generic types -- then demonstrate implicit/explicit operator user-defined conversion patterns.
Add the following declarations:
public abstract class BaseProfile<TPerson>
where TPerson : PersonDetails<GenericDetails>, new()
{
public TPerson? Person { get; set; } = new TPerson();
public virtual void UpdateDetailsId(int id)
{
Person!.Details!.Id = id;
}
public static explicit operator Profile1(BaseProfile<TPerson> details)
{
var profile = new Profile1();
profile.Person!.Details = (GenericDetails)details.Person!.Details!;
return profile;
}
public static explicit operator Profile2(BaseProfile<TPerson> details)
{
var profile = new Profile2();
profile.Person!.Details = (GenericDetails)details.Person!.Details!;
return profile;
}
}
public class GenericProfile : BaseProfile<GenericPerson>
{
}
public abstract class GenericPersonDetails : PersonDetails<GenericDetails>
{
}
public class GenericPerson : GenericPersonDetails
{
}
public class GenericDetails : IDetails
{
public int Id { get; set; }
public static implicit operator DetailsA(GenericDetails details)
{
return new DetailsA() { Id = details.Id };
}
public static implicit operator DetailsB(GenericDetails details)
{
return new DetailsB() { Id = details.Id };
}
}
and, update the testing functional scope:
var profile1 = new Profile1();
var profile2 = new Profile2();
var genericProfile = new GenericProfile();
profile1.UpdateDetailsId(10);
profile2.UpdateDetailsId(12);
genericProfile.UpdateDetailsId(20);
Console.WriteLine(profile1.Person!.Details!.Id);
Console.WriteLine(profile1.Person!.Details!.FirstName ?? "No First Name");
Console.WriteLine(profile2.Person!.Details!.Id);
Console.WriteLine(profile2.Person!.Details!.LastName ?? "No Last Name");
Console.WriteLine(genericProfile.Person!.Details!.Id);
Console.WriteLine(((Profile1)genericProfile).Person!.Details!.FirstName ?? "No First Name");
Console.WriteLine(((Profile2)genericProfile).Person!.Details!.LastName ?? "No Last Name");
Console.WriteLine();

List of Class with Property of T

I am trying to create a list of a class with the property with T:
class Test<T> where T : IClass, new()
{
public T Actor { get { return new T(); } }
public eEnum { get; set; }
public String Str { get; set; }
}
The above is an example class, how can I create a list of the above class?
I have tried the below with no avail:
List<Test<IClass>> list = new List<IClass>();
Is there a way to achieve creating a list like I am trying to generate?
Since you have added the generic type constraint new(), you must provide a type that has a public parameterless constructor. An interface doesn't have a constructor. Therefore you must indicate a class. E.g.
List<Test<MyActorClass>> list = new List<Test<MyActorClass>>();
Or drop this new() constraint and instead add a generic factory method
class Test
{
public T CreateActor<T>()
where T : IClass, new()
{
return new T();
}
public MyEnum eEnum { get; set; }
public string Str { get; set; }
}
And simply create a list List<Test>.
Or supply a concrete actor type through constructor injection:
class Test
{
public Test(IClass actor)
{
Actor = actor;
}
public IClass Actor { get; }
public MyEnum eEnum { get; set; }
public string Str { get; set; }
}
An even more advanced construction is to use a non-generic abstract base class and to derive a generic one from it
abstract class Test
{
private IClass _actor;
public IClass Actor
{
get {
if (_actor == null) {
_actor = CreateActor();
}
return _actor;
}
}
public MyEnum eEnum { get; set; }
public string Str { get; set; }
protected abstract IClass CreateActor(); // We implement it in the generic class.
}
class Test<T> : Test
where T : IClass, new()
{
public new T Actor // Hides inherited member.
{
get { return (T)base.Actor; }
}
protected override IClass CreateActor()
{
return new T();
}
}
The list would again be of type List<Test>. This has the advantage that you can add different types of classes deriving from Test to the list and at the same time you have a strongly typed actor when accessing it through a concrete Test<T>.

Class with generic property MUST be generic?

Let's say I have this class
public class Foo<T>
{
public List<T> FooList { get; set;}
}
And this class is used by another class like:
public class Bar
{
public List<Foo> Foos { get; set; } // THIS DOESNT COMPILE
}
The above solution doesn't work because it says my generic type Foo requires 1 type argument.
In my real scenario, I don't think it makes much sense to propagate the generic to Bar class, like:
public class Bar<T>
{
public List<Foo<T>> Foos { get; set; } // Not the solution I want
}
So I would like to know: which is the best solution here, if any? Do I really need to make my Bar class generic? In Java, if Im not mistaken, I could do something like List<Foo<?>> Foos and it would work...
Edit
So, as asked, I'm giving the concrete example.
I am doing some DTO classes for Highcharts.
Highcharts, among a lot of other objects, has the series object. This series object have the data property which can be pretty much anything: A list of numbers, a list of pairs of numbers, etc, etc.
So my generic class would be the series:
public class SeriesDto<T>
{
public List<T> Data { get; set; }
// Other options ...
}
And it would be used by the HighchartsDto class:
public class HighchartsDto {
// Lot of things..
// ...
public List<SeriesDto> Series { get; set; } // doesn't compile
}
It doesn't make sense to make HighchartsDto generic because the same chart can have different types of series.
You can add an interface:
public interface IFoo
{
}
public class Foo<T> : IFoo
{
public List<T> FooList { get; set; }
}
public class Bar
{
public List<IFoo> Foos { get; set; }
}
The trouble is you haven't given a realistic example, just a hypothetical.
The following doesn't compile, because it doesn't knows what generic type you want to use? what should it guess?
public class Bar
{
public List<Foo> Foos { get; set; } // THIS DOESNT COMPILE
}
If you understand Bar, then you could do something lie this and mark the type in advance, i.e Bar will always implement Foo<int>
public class Bar
{
public List<Foo<int>> Foos { get; set; }
}
If you want more power, and to choose the type at a later point in code, you can do this
public class Bar<T>
{
public List<Foo<T>> Foos { get; set; }
}
Or as Backs pointed out you can add an interface, all this is really doing though is hiding the members of Foo<T>, List<T> FooList { get; set;} is unusable in this context.
You need to work out what want, and why you want it, and maybe better explain it
"This series object have the data property which can be pretty much anything: A list of numbers, a list of pairs of numbers, etc, etc."
A List<T> is not a good for a collection that can be anything. A List<T> is a collection of one thing of type T. Maybe you want the good old ArrayList which is a collection of object, and thus it can be anything. In essence you have to decide which is a common type the contents can have.
Contents have nothing in common, other than deriving from object
public class SeriesDto
{
public ArrayList Data { get; set; }
// Other options ...
}
public class HighchartsDto
{
// Lot of things..
// ...
public List<SeriesDto> Series { get; set; } // doesn't compile
}
class Program
{
static void Main(string[] args)
{
var dto = new SeriesDto();
dto.Data.Add("A string");
dto.Data.Add(1001);
dto.Data.Add(new TimeSpan(0, 0, 15));
var list = new HighchartsDto();
list.Series.Add(dto);
TimeSpan time = (TimeSpan)list.Series[0].Data[2]; // 15 seconds
}
}
Contents have a basic interface in common which includes all properties and methods that is shared with all types
public interface ISeriesData
{
DateTime CreatedTime { get; }
}
public class StringData : ISeriesData
{
public StringData(string name)
{
this.Name = name;
this.CreatedTime = DateTime.Now;
}
public string Name { get; }
public DateTime CreatedTime { get; }
}
public class IntegerData : ISeriesData
{
public IntegerData(int value)
{
this.Value = value;
this.CreatedTime = DateTime.Now;
}
public DateTime CreatedTime { get; }
public int Value { get; }
}
public class SeriesDto
{
public List<ISeriesData> Data { get; set; }
// Other options ...
}
public class HighchartsDto
{
// Lot of things..
// ...
public List<SeriesDto> Series { get; set; } // doesn't compile
}
class Program
{
static void Main(string[] args)
{
var a = new StringData("A");
var b = new IntegerData(1001);
var dto = new SeriesDto();
dto.Data.Add(a);
dto.Data.Add(b);
var list = new HighchartsDto();
list.Series.Add(dto);
DateTime time = list.Series[0].Data[0].CreatedTime; // time
}
}
Make HighChartsDto Generic and inherit from it the concrete classes.
public class SeriesDto<T>
{
public List<T> Data { get; set; }
// Other options ...
}
public class HighChartsDto<L, T> where L : SeriesDto<T>
{
// Lot of things..
// ...
public int SomethingElse { get; set; }
public List<L> Series { get; set; } // doesn't compile
}
public class HighChartsStringDto : HighChartsDto< SeriesDto<string>, string>
{
}
class Program
{
static void Main(string[] args)
{
var a = new SeriesDto<string>();
a.Data.Add("A");
a.Data.Add("B");
a.Data.Add("C");
var b = new SeriesDto<string>();
b.Data.Add("1");
b.Data.Add("2");
b.Data.Add("3");
var list = new HighChartsStringDto();
list.Series.Add(a);
list.Series.Add(b);
string x = list.Series[1].Data[2]; // "C"
}
}
Althoug I'm aware that this solution is flawed, this would be another way to do this.
public class Foo<T> : Foo
{
public List<T> FooList { get; set; }
}
public class Foo
{
}
So you're creating a non-generic class from which your generic Foo derives.
Then you can do this:
public class Bar
{
public List<Foo> Foos { get; set; } // THIS DOES NOW COMPILE
}
And then if needed you could cast the non-generic List to a list of your generic-type like this:
bar.Foos.Cast<Foo<GENERICTYPE>>()
But that requires you knowing which exact type it is going to be.

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

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?
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);
}
}
Another approach will be overload methods.
Because you have different logic based on the given type. And types have nothing in common (interface/abstract class).
public class NotificationHelper
{
public bool SendNotification(obj1 obj)
{
var sender = new SendSMS();
return sender.Send(obj);
}
public bool SendNotification(obj2 obj)
{
var sender = new SendPush();
return sender.Send(obj);
}
public bool SendNotification(obj3 obj)
{
var sender = new SendEmail();
return sender.Send(obj);
}
}
Then using will be clear enough
var someObject = GetObjectFromSomeWhere();
var isSuccessful = SendNotification(someObject);
I would suggest creating a parent class from which these 3 inherit
public class ParentType { }
public class Obj1 : ParentType { ... }
The method would then just request the parent type, such as:
public bool SendNotification(ParentType obj) { ... }

Categories