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();
}
}
}
Related
Say I have a class like...
public abstract class Base
{
public abstract IAttributes Attributes{ get; set; }
}
public interface IAttributes
{
string GlobalId { get; set; }
}
And a class like this...
public class ImplementAttributes : IAttributes
{
public string GlobalId { get; set; } = "";
public string LocalId { get; set; } = "";
// Other Properties and Methods....
}
And then I implement it like...
public class Derived: Base
{
public new ImplementAttributes Attributes { get; set; }
}
Now, I realise the above will not work because I can't override the property Attributes and if I hide it with new then the following bellow is null because the Base property does not get written.
public void DoSomethingWithAttributes(Base base)
{
var Foo = FindFoo(base.Attributes.GlobalId); // Null because its hidden
}
But I would like to be able to access the Base and Derived property attributes eventually like Above.
Can this be accomplished? Is there a better way?
You can use generics:
public abstract class Base<T> where T: IAttributes
{
public abstract T Attributes{ get; set; }
}
public interface IAttributes
{
string GlobalId { get; set; }
}
And
public class Derived: Base<ImplementAttributes>
{
public override ImplementAttributes Attributes { get; set; }
}
And then:
public void DoSomethingWithAttributes<T>(Base<T> b) where T : IAttributes
{
var Foo = FindFoo(b.Attributes.GlobalId);
}
You can pass Derived instances without specifying a type parameter explicitly:
Derived d = new Derived();
DoSomethingWithAttributes(d);
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.
Short version:
How can I best avoid using multiple type parameters? According to this it is a bad idea.
Long version:
I have 3 interfaces. I have multiple classes implementing these interfaces. It looks something like this:
public interface IMyInterface1 { }
public interface IMyInterface2 { }
public interface IMyInterface3 { }
public class MyClass1A : IMyInterface1 { public int Id { get; set; } }
public class MyClass1B : IMyInterface1 { }
public class MyClass1C : IMyInterface1 { }
public class MyClass2A : IMyInterface2 { }
public class MyClass2B : IMyInterface2 { }
public class MyClass2C : IMyInterface2 { }
public class MyClass3A : IMyInterface3 { }
public class MyClass3B : IMyInterface3 { }
public class MyClass3C : IMyInterface3 { }
Originally I had a base class and a class that derives from it with multiple type parameters like this:
public abstract class MyBaseClass<T, U, V>
where T : IMyInterface1
where U : IMyInterface2
where V : IMyInterface3
{
public T ConcreteMyInterface1 { get; set; }
public U ConcreteMyInterface2 { get; set; }
public V ConcreteMyInterface3 { get; set; }
public abstract T DoStuffWithInterface1();
}
public class MyClass : MyBaseClass<MyClass1A, MyClass2A, MyClass3A>
{
public override MyClass1A DoStuffWithInterface1()
{
var id = ConcreteMyInterface1.Id;
return ConcreteMyInterface1;
}
}
In an effort to remove the type parameters I refactored the code but it resulted in me having to cast everywhere like this:
public abstract class MyBaseClass
{
public IMyInterface1 ConcreteMyInterface1 { get; set; }
public IMyInterface2 ConcreteMyInterface2 { get; set; }
public IMyInterface3 ConcreteMyInterface3 { get; set; }
public abstract IMyInterface1 DoStuffWithInterface1();
}
public class MyClass : MyBaseClass
{
public MyClass()
{
ConcreteMyInterface1 = new MyClass1A();
ConcreteMyInterface2 = new MyClass2A();
ConcreteMyInterface3 = new MyClass3A();
}
public override IMyInterface1 DoStuffWithInterface1()
{
var id = ((MyClass1A)ConcreteMyInterface1).Id;
return ConcreteMyInterface1;
}
}
I was hoping for something more helpful than what Microsoft had to say in the above linked article:
To fix a violation of this rule, change the design to use no more than two type parameters.
When I use a generic interface, which is an overload of a normal interface I get an error the property of the normal interface is not implemented.
Example:
public interface IInterface1
{
string Bar1{ get; set; }
string Bar2{ get; set; }
}
public interface IInterface2
{
IInterface1 Foo{ get; }
}
public interface IInterface3<T>:IInterface2 where T:IInterface1
{
new T Foo { get; set; }
}
public class Class1<T> : IInterface3<T> where T : IInterface1
{
public T Foo { get; set; }
}
ERROR: 'Class1' does not implement interface member 'IInterface2.Foo'. 'Class1.Foo' cannot implement 'IInterface2.Foo' because it does not have the matching return type of 'IInterface1'.
But T is always of type IInterface1. What am I doing wrong?
Edit1:
If I remove the IInterface3, I cannot return T from my class
Edit2:
When I change Foo of Class1 to return IInterface1 instead of T, I'll still get an error
Edit3:
When I change IInterface3 and Class1 to return the same object in 2 ways it does work.
I only want Foo and Foo1 in Class1 combined.
(btw: removed the setter of IInterface2. I don't need it)
Solution:
The answers of fejesjoco and Rik got me thinking and helped me in this final solution:
public interface IInterface1
{
string Bar1 { get; set; }
string Bar2 { get; set; }
}
public interface IInterface2
{
IInterface1 Foo { get; }
}
public interface IInterface3<T>:IInterface2 where T:IInterface1
{
new T Foo { get; set; }
}
public class Class1<T> : IInterface3<T> where T : IInterface1
{
private T _foo;
T IInterface3<T>.Foo { get { return _foo; } set { _foo = value; } }
IInterface1 IInterface2.Foo { get { return _foo; } }
}
or
public class Class1<T> : IInterface3<T> where T : IInterface1
{
public T Foo { get; set; }
IInterface1 IInterface2.Foo { get { return Foo; } }
}
The prove of the pudding is in the tasting:
public class ImplemetationA : IInterface1
{
public string Bar1 { get; set; }
public string Bar2 { get; set; }
// some extra definitions here
}
public class ImplemetationB : IInterface1
{
public string Bar1 { get; set; }
public string Bar2 { get; set; }
// some extra definitions here
}
public class Problem : Class1<ImplemetationA>
{
public ImplemetationA Foo { get; set; }
}
public class ProblemSolved
{
public IInterface2 Method1()
{
var solvedProblem = new Problem();
solvedProblem.Foo = new ImplemetationA();
return solvedProblem;
}
public void Method2()
{
IInterface2 solvedProblem = Method1();
var result = solvedProblem.Foo;
}
}
But T is always of type IInterface1
Not exactly. T will be a type that implements IInterface1, but it will never be exactly IInterface1. Member signatures must match the interface specification exactly. You have to implement a property with the name Foo and type IInterface1. You can use explicit interface implementation and call into the other Foo, this way they will work the same way.
Edit: I think what you are looking for is this:
public interface IInterface1
{
string Bar1{ get; set; }
string Bar2{ get; set; }
}
public interface IInterface2<T> where T : IInterface1
{
T Foo{ get; set; }
}
public class Class1<T> : IInterface2<T> where T : IInterface1
{
public T Foo { get; set; }
}
Edit2
If you need to also have a non-generic interface, you can try this:
public interface IInterface1
{
string Bar1{ get; set; }
string Bar2{ get; set; }
}
public interface IInterface2
{
IInterface1 Foo{ get; }
}
public interface IInterface3<T>:IInterface2 where T:IInterface1
{
new T Foo { get; set; } // hide the non-generic IInterface2.Foo
}
public class Class1<T> : IInterface3<T> where T : IInterface1
{
public T Foo { get; set; }
IInterface1 IInterface2.Foo { get { return Foo; }} // explicitely implement IInterface2.Foo
}
This compiles, but I'm not 100% sure this also gives the desired behavior.
Original Answer
I was typing almost exactly what fejesjoco already answered. I'd just like to illustrate why this is a problem.
Consider a few additional classes:
public class ImplemetationA : IInterface1
{
string Bar1{ get; set; }
string Bar2{ get; set; }
// some extra definitions here
}
public class ImplemetationB : IInterface1
{
string Bar1{ get; set; }
string Bar2{ get; set; }
// some extra definitions here
}
public class Problem : Class1<ImplemetationA>
{
public ImplemetationA Foo {get; set;}
}
So the class Problem implements the interface as you defined it. The issue is that Problem still has to implement the non-generic interface IInterface2. According to your logic, this requirement is fulfilled by the already present property Foo in class Problem, because "T is always of type IInterface1". But what happens why we try to do this:
var myProblem = new Problem(); // remember, Problem : Class1<ImplemetationA>
var myImplB = new ImplementationB();
myProblem.Foo = myImplB;
Problem implements IInterface2, and since ImplementationB implements IInterface1, this should be allowed, but obviously, there's a type conflict here because there is no implicit conversion between ImplemetationB and ImplemetationA.
Given this code:
public interface ITagModel { }
public interface ITemplate {
ITagModel Model { get; set; }
}
public class EmailTag : ITagModel { }
public class EmailTest : ITemplate {
public EmailTag Model { get; set; }
}
I am being told that the Type of EmailTag (inside EmailClass) cannot implement the Property Model because it is not the type ITagModel.
It is inheriting ITagModel....so why won't this work? What can I do to accomplish what I'm looking for?
C# doesn't support covariant return.
e.g.
public class Base { }
public class Derived : Base { }
public class Component
{
public virtual Base GetComponent()
{
return new Base();
}
}
public class DerviedComponent : Component
{
public override Dervied GetComponent()
{
return new Derived();
}
}
So you'll need to abstract the return type and constrain it to your interface. That way any implementation will need to supply an ITagModel.
public interface ITagModel { }
public interface ITemplate<TModel>
where TModel : ITagModel
{
TModel Model { get; set; }
}
public class EmailTag : ITagModel { }
public class EmailTest : ITemplate<EmailTag> {
public EmailTag Model { get; set; }
}
It sounds like your ITemplate interface should be generic:
public interface ITemplate<TModel> where TModel : ITagModel
{
TModel Model { get; set; }
}
Then:
public class EmailTest : ITemplate<EmailTag>
{
public EmailTag Model { get; set; }
}
Imagine if your current code worked. I could then write:
ITemplate template = new EmailTest();
template.Model = new SomeOtherModel();
... and you wouldn't want that. (Basically, you'd be violating type safety.)