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.
Related
I'm creating a library for my Socket TCP servers.
I want my BaseServer class to take a BaseUser as generic type and the BaseUser to take a BaseServer as generic type (they are abstract classes).
Both classes will need their type in the where clause of the type.
How can i manage to make it work ?
BaseServer.cs
public abstract class BaseServer<UserType> where UserType : BaseUser//Need <BaseServerChildrenType>
{
public List<UserType> Users { get; protected set; }
}
BaseUser.cs
public abstract class BaseUser<ServerType> where ServerType : BaseServer//Need <BaseUserChildrenType>
{
public ServerType server { get; private set; }
}
Maybe that can help:
public interface IBaseUser
{ }
public interface IBaseServer
{ }
public abstract class BaseServer<UserType> where UserType : IBaseUser//Need <BaseServerChildrenType>
{
public List<UserType> Users { get; protected set; }
}
public abstract class BaseUser<ServerType> where ServerType : IBaseServer//Need <BaseUserChildrenType>
{
public ServerType server { get; private set; }
}
public class Server1 : BaseServer<User1>, IBaseServer
{ }
public class User1 : BaseUser<Server1>, IBaseUser
{ }
Option 2, so you don't need to implement the interface in any deriverd class:
public interface IBaseUser
{ }
public interface IBaseServer
{ }
public abstract class BaseServer<UserType> :IBaseServer where UserType : IBaseUser
{
public List<UserType> Users { get; protected set; }
}
public abstract class BaseUser<ServerType> :IBaseUser where ServerType : IBaseServer
{
public ServerType server { get; private set; }
}
public class Server1 : BaseServer<User1>
{ }
public class User1 : BaseUser<Server1>
{ }
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();
}
}
}
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'm currently trying to assign an ObservableCollection of an object (in the example below: ObservableCollection<A>), to a field in a parent class that is an ObservableCollection of an interface implemented by the object (in the example below: public ObservableCollection<IOrderable> childModels, where type A implements IOrderable).
This can be seen in the following:
public class A : IOrderable { }
public class B : IOrderable { }
public class Term
{
public ObservableCollection<A> aCollection { get; set; }
}
public class Year
{
public ObservableCollection<B> bCollection { get; set; }
}
public abstract class InfoListViewModelBase : ViewModelBase
{
public ObservableCollection<IOrderable> childModels { get; set; }
}
public class TermViewModel : InfoListViewModelBase
{
public TermViewModel(Term t)
{
this.childModels = t.aCollection;
}
}
public class YearViewModel : InfoListViewModelBase
{
public TermViewModel(Year y)
{
this.childModels = y.bCollection;
}
}
The issue is that this.childModels = t.aCollection; and this.childModels = y.bCollection; both do not work, instead giving the error "Cannot implicitly convert type ObservableCollection<A> to ObservableCollection<IOrderable>" and "Cannot implicitly convert type ObservableCollection<B> to ObservableCollection<IOrderable>"
Is there a correct way of achieving this desired result between the classes?
You could make your InfoListViewModelBase generic.
public abstract class InfoListViewModelBase<T> : ViewModelBase where T:IOrderable
{
public ObservableCollection<T> childModels { get; set; }
}
Your implementations become
public class TermViewModel : InfoListViewModelBase<A> { }
public class YearViewModel : InfoListViewModelBase<B> { }
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.)