I have base Interface:
public interface IDebtBase
{
// with properties
}
which is inherited by another interface
public interface IDebt : IDebtBase
{
// with properties
}
Another interface defines the IDebt interface in a list:
public interface ILoanBase
{
List<IDebtBase> Debts { get; set; }
}
which is inherited for another interface ILoan:
public interface ILoan : ILoanBase
{
// with properties
}
The Loan object implements the ILoan:
public class Loan : ILoan
{
List<IDebt> Debts { get; set; }
}
For full reference, the Debt object implements IDebt:
public class Debt : IDebt
{
// with properties
}
I receive an error that says that List in Loan needs to be IDebtBase not IDebt:
'Loan' does not implement interface member 'ILoanBase.Debts'
Should this work, or does Interface inheritance not work this way?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Loan l = new Loan();
var a = 1;
}
}
public interface IDebtBase
{
// with properties
}
public interface IDebt : IDebtBase
{
// with properties
}
public interface ILoanBase<T> where T : IDebtBase
{
List<T> Debts { get; set; }
}
public interface ILoan : ILoanBase<IDebt>
{
// with properties
}
public class Loan : ILoan
{
public List<IDebt> Debts { get; set; }
}
}
I think what you are trying to do is declare a list of interfaces but actually implement a list of concrete types. This is tricky to do with a static language like C#. You best choice is to use generics for the loan base interface.
public interface IDebtBase
{
decimal Amount { get; set; }
}
public interface IDebt : IDebtBase
{
int ID { get; set; }
}
public interface ILoanBase<TDebt> where TDebt : IDebtBase
{
List<TDebt> Debts { get; set; }
}
public interface ILoan : ILoanBase<IDebt>
{
decimal Rate { get; set; }
}
public class Debt : IDebt
{
public int ID { get; set; }
public decimal Amount { get; set; }
}
public class Loan : ILoan
{
public static decimal DefaultRate=0.18m;
public Loan()
{
this.Debts=new List<IDebt>();
this.Rate=DefaultRate;
}
public List<IDebt> Debts { get; set; }
public decimal Rate { get; set; }
public void AddDebt(int id, decimal amount)
{
Debts.Add(new Debt() { ID=id, Amount=amount });
}
}
class Program
{
static void Main(string[] args)
{
var loan=new Loan() { Rate=0.22m };
loan.AddDebt(1002, 5400m);
}
}
The drawback is than not all loans derive from one type. That is because ILoadBase<IDebt> is different from ILoadBase<IDebtBase>. IN my experience building a hierarchy tree with interfaces is very tricky once you start having collections involved.
An alternative to the above is to change the loan type to use the concrete debt class
public interface ILoan : ILoanBase<Debt> // change from ILoanBase<IDebt>
{
decimal Rate { get; set; }
}
public class Loan : ILoan
{
public static decimal DefaultRate=0.18m;
public Loan()
{
this.Debts=new List<Debt>(); // change from List<IDebt>
this.Rate=DefaultRate;
}
public List<Debt> Debts { get; set; } // Change from List<IDebt>
public decimal Rate { get; set; }
public void AddDebt(int id, decimal amount)
{
Debts.Add(new Debt() { ID=id, Amount=amount });
}
}
which will give you in the end a List<Debt> collection (which I assume is what you desire). Now to make it more useful create a common base loan class without generics
public interface ILoanBase
{
string Issuer { get; set; }
IList<IDebtBase> GetDebts();
}
and use it for every loan interface. Finally in the concrete loan class you need to implement GetDebts() which would convert the List<Debt> into List<IDebtBase>.
public interface ILoanBase<TDebt> : ILoanBase
where TDebt : IDebtBase
{
List<TDebt> Debts { get; set; }
}
public class Loan : ILoan
{
...
public IList<IDebtBase> GetDebts()
{
return Debts.OfType<IDebtBase>().ToList();
}
}
now you have collect all the loans into a single collection
static void Main(string[] args)
{
List<ILoanBase> loans=new List<ILoanBase>();
//populate list
var abc=new Loan() { Issuer="ABC", Rate=0.22m };
abc.AddDebt(1002, 5400m);
loans.Add(abc);
//iterate list
foreach (var loan in loans)
{
Console.WriteLine(loan.Issuer);
foreach (var debt in loan.GetDebts())
{
Console.WriteLine(debt.Amount.ToString("C"));
}
}
// ABC
// $5,400.00
}
Let me explain why this won't work.
Assume I add another class Foo : IDebtBase and I execute
myLoan.Debts.Add(New Foo());
Adding a Foo works on a List<IDebtBase> (i.e., it works on ILoanBase.Debts), but not on a List<IDebt> (i.e., it won't work on Loan.Debts).
Thus, if the compiler allowed you to implement ILoanBase in Loan in the way you proposed, the Liskov substitution principle would be violated.
Related
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();
I have the following construction of classes, here simplified as child classes of a 'mother' class called DataClass, which also contains one simple method:
public class DataClass
{
public int num { get; set; }
public string code { get; set; }
public PartClass part { get; set; }
public MemberClass member { get; set; }
public int Count()
{
Type t = typeof(DataClass);
return typeof(DataClass).GetProperties().Length;
}
}
public class PartClass
{
public int seriesNum { get; set; }
public string seriesCode { get; set; }
}
public class MemberClass
{
public int versionNum { get; set; }
public SideClass side { get; set; }
}
public class SideClass
{
public string firstDetail { get; set; }
public string secondDetail { get; set; }
public bool include { get; set; }
}
The issue is, I want to refactor the method so that it can give me an accurate counting of all properties found, including the ones in nested or child classes. In the above example, it only counts properties of DataClass, while I wanted it to return 2 for DataClass + 2 for PartClass + 1 for MemberClass + 3 for SideClass, sums up to 8 properties you may set through DataClass.
Can someone help me with this?
You can introduce interface with Count() method
public interface ICountable
{
int Count();
}
And use this interface to mark all types, which properties are participating in Count() calculation.
You can see the generic abstract class to implement this interface below. Generic T parameter is type whose properties need to be calculated. You implement a calculation logic only once and inherit this class where needed. You also go through all of properties, implementing ICountable, to calculate them as well (some kind of recursion)
public abstract class Countable<T> : ICountable
{
public int Count()
{
Type t = typeof(T);
var properties = t.GetProperties();
var countable = properties.Select(p => p.PropertyType).Where(p => typeof(ICountable).IsAssignableFrom(p));
var sum = countable.Sum(c => c.GetProperties().Length);
return properties.Length + sum;
}
}
and inherit it in your classes
public class DataClass : Countable<DataClass>
{
...
}
public class PartClass : Countable<PartClass>
{
...
}
public class MemberClass : Countable<MemberClass>
{
...
}
public class SideClass : Countable<SideClass>
{
...
}
And this is for the test
var dataClass = new DataClass();
var count = dataClass.Count();
It returns 8 as expected
So, I want to create an interface which has a method that can take in any model class. For example
I have these three property class
class A
{
public long id { get; set; }
public string description { get; set; }
public string code { get; set; }
}
class B
{
public long someID { get; set; }
}
class C
{
public long anydesign { get; set; }
}
class D
{
public long Router { get; set; }
}
I have an interface
public interface IModel
{
void Dosomething(A model); // Now in this example it takes the A model,But I want it to be set, so that that class that implements the interface can put any model as required
}
Now, I have a class that implements the mode Since the interface only takes the A model, I can pass in the A model in the class during implementation
public class ImplemenationA: IModel
{
public void Dosomething(A model)
{
Console.WriteLine(model.description);
}
}
Say i have another implemenation Class Now, I am guessing the below one wouldnt work, as the interface signature enforces only to take a Model A and not any other model
public class ImplementationB:IModel
{
public void Dosomething(B model)
{
Console.WriteLine(model.someID);
}
}
I want to the interface method to be invoked by any implementation class and use any model
Based on your description you may want to use Generics. Since you're creating separate implementations you can apply the interface below to achieve a similar result.
public interface IModel<T>
{
void Dosomething(T model);
}
public class ImplementationB : IModel<B>
{
public void Dosomething(B model)
{
Console.WriteLine(model.someID);
}
}
Some sort of Decorator pattern can solve it, postpone your actual implementation to inner classes and insist on separation of concern, please leave a comment if I miss understood your question:
//added to support inner implementation
interface IModelImpl {
void Do();
}
class A: IModelImpl
{
public long id { get; set; }
public string description { get; set; }
public string code { get; set; }
public void Do(){
console.WriteLine(this.description);
}
}
class B: IModelImpl
{
public long someID { get; set; }
public void Do(){
console.WriteLine(this.someID);
}
}
class C: IModelImpl
{
public long anydesign { get; set; }
public void Do(){
...
}
}
Here is your IModel, pretty much the same, considered like an outer implementation:
public interface IModel
{
void Dosomething(IModelImpl model); //put any model as long it implements IModelImpl
}
Your class implementation should now changed to:
public class ImplemenationA: IModel
{
public void Dosomething(IModelImpl model)
{
//Do more specific work to ImplementationA
model.Do();
}
}
Another class implementation:
public class ImplementationB:IModel
{
public void Dosomething(IModelImpl model)
{
//Do more specific work to ImplementationB like validation
model.Do();
}
}
I have interfaces IChild and IParent. IParent has a member that is a List<IChild>.
I wish to have classes that implement IParent where each class has a member that implements IChild:
public interface IChild
{
}
public interface IParent
{
List<IChild> a { get; set; }
}
public class ChildA : IChild
{
}
public class ChildB : IChild
{
}
public class ParentA : IParent
{
public List<ChildA> a { get; set; }
}
public class ParentB : IParent
{
public List<ChildB> a { get; set; }
}
But, this code will not compile. The error is:
`MyApp.Data.ParentA` does not implement interface member `MyApp.Data.IParent.a`.
`MyApp.Data.ParentA.a` cannot implement `MyApp.Data.IParent.a` because it does not have
the matching return type of `System.Collections.Generic.List<MyApp.Data.IChild>`.
Make IParent generic:
public interface IChild
{
}
public interface IParent<TChild> where TChild : IChild
{
List<TChild> a { get; set; }
}
public class ChildA : IChild { }
public class ChildB : IChild { }
public class ParentA : IParent<ChildA>
{
public List<ChildA> a { get; set; }
}
public class ParentB : IParent<ChildB>
{
public List<ChildB> a { get; set; }
}
The implementation can only return List of IChild as follows:
public interface IChild
{
}
public interface IParent
{
List<IChild> Children { get; set; }
}
public class ChildA : IChild
{
}
public class ChildB : IChild
{
}
public class ParentA : IParent
{
public List<IChild> Children
{
get;
set;
}
}
public class ParentB : IParent
{
public List<IChild> Children
{
get;
set;
}
}
You need to have the classes return a List<IChild>:
public class ParentA : IParent
{
public List<IChild> a { get; set; }
}
public class ParentB : IParent
{
public List<IChild> a { get; set; }
}
A collection of IChild cannot be implicitly converted to a collection of its child type
Change the return type of IParent.a to List<ChildA> OR change the property declaration on ParentA and ParentB to public List<IChild> a { get; set; }. I recommend the latter, as I think that is what you're most likely going for.
I had a similar requirement where I had two different methods that operated on two different classes but had the same logic in it for the properties that are common to both the classes.
so I thought to use inheritance and generics for this to write a common method, I was able to achieve in the following way.
namespace OOPS.Interfaces
{
using System.Collections.Generic;
public interface IBanner
{
string Name { get; set; }
}
public interface IBannerContent<T> where T : IBanner
{
List<T> Banners { get; set; }
}
}
Simple Model.
namespace OOPS.Simple
{
using Interfaces;
using System.Collections.Generic;
public class Banner : IBanner
{
public string Name { get; set; }
}
public class BannerContent : IBannerContent<Banner>
{
public List<Banner> Banners { get; set; }
}
}
Complex Model.
namespace OOPS.Complex
{
using Interfaces;
using System.Collections.Generic;
public class Banner : IBanner
{
public string Name { get; set; }
public string Description { get; set; }
}
public class BannerContent : IBannerContent<Banner>
{
public List<Banner> Banners { get; set; }
}
}
The common business logic and sample invocation. The key part here is using the where clause to constrain the type such as where T : IBanner all the way down till the method we want it to be common.
namespace OOPS
{
using Interfaces;
using System;
using System.Collections.Generic;
public class BusinessLogic
{
public void Print<T>(IBannerContent<T> bannerContent) where T : IBanner
{
foreach (var item in bannerContent.Banners)
{
Console.WriteLine(item.Name);
}
}
}
class Program
{
static void Main(string[] args)
{
var banner1 = new Simple.BannerContent
{
Banners = new List<Simple.Banner>
{
new Simple.Banner { Name = "Banner 1" },
new Simple.Banner { Name = "Banner 2" }
}
};
var banner2 = new Complex.BannerContent
{
Banners = new List<Complex.Banner>
{
new Complex.Banner { Name = "Banner 3", Description = "Test Banner" },
new Complex.Banner { Name = "Banner 4", Description = "Design Banner" }
}
};
var business = new BusinessLogic();
business.Print(banner1);
business.Print(banner2);
Console.ReadLine();
}
}
}
Let's say I have nested generic data classes similar to the following:
public class BaseRecordList<TRecord, TUserInfo>
where TRecord : BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual IList<TRecord> Records { get; set; }
public virtual int Limit { get; set; }
}
public class BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
public class BaseUserInfo
{
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
With 2 concrete versions like so:
// Project 1: Requires some extra properties
public class Project1RecordList : BaseRecordList<Project1Record, Project1UserInfo> {}
public class Project1Record : BaseRecord<Project1UserInfo>
{
public Guid Version { get; set; }
}
public class Project1UserInfo : BaseUserInfo
{
public string FavouriteFood { get; set; }
}
and
// Project 2: Some properties need alternate names for JSON serialization
public class Project2RecordList : BaseRecordList<Project2Record, Project2UserInfo>
{
[JsonProperty("allRecords")]
public override IList<Project2Record> Records { get; set; }
}
public class Project2Record : BaseRecord<Project2UserInfo> {}
public class Project2UserInfo : BaseUserInfo
{
[JsonProperty("username")]
public override string Name { get; set; }
}
I'm then happy to have 2 repositories that return Project1RecordList and Project2RecordList respectively, but at some point in my code I find myself needing to be able to handle both of these in one place. I figure that at this point I need to be able to treat both of these types as
BaseRecordList<BaseRecord<BaseUserInfo>, BaseUserInfo>
as this is the minimum required to meet the generic constraints, but trying to cast or use "as" throws up errors about not being able to convert.
Is there any way to do this, or even a more sane way to handle this situation without massive amounts of code duplication? If it makes any difference this is for a web app and there are already a large number of data classes, many of which use these nested generics.
What you are talking about is called covariance and MSDN has a great article on this here: https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
First, create a new interface:
interface IBaseRecord<out TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecord inherit from the new interface:
public class BaseRecord<TUserInfo> : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
If done right, this should compile:
IBaseRecord<BaseUserInfo> project1 = new Project1Record();
IBaseRecord<BaseUserInfo> project2 = new Project2Record();
To expand this to the BaseRecordList, create IBaseRecordList:
interface IBaseRecordList<out TRecord, out TUserInfo>
where TRecord : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecordList inherit from that:
public class BaseRecordList<TRecord, TUserInfo> : IBaseRecordList<TRecord, TUserInfo>
And then use as such:
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project1 = new Project1RecordList();
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project2 = new Project2RecordList();
Once you have that setup, just add whatever properties or functions you need to use generically to the interfaces.