This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Interface or abstract class?
I have a group of classes defined as follows:
namespace VGADevices.UsingAbstractClass
{
public abstract class VGA
{
public abstract int HorizontalResolution { get; set; }
public abstract int VerticalResolution { get; set; }
}
public class LCDScreen : VGA
{
public override int HorizontalResolution { get; set; }
public override int VerticalResolution { get; set; }
}
} // namespace VGADevices.UsingAbstractClass
namespace VGADevices.UsingInterfaces
{
public interface IVGA
{
int HorizontalResolution { get; set; }
int VerticalResolution { get; set; }
}
public class LCDScreen : IVGA
{
public virtual int HorizontalResolution { get; set; }
public virtual int VerticalResolution { get; set; }
}
} // namespace VGADevices.UsingInterfaces
Client code, I have the choice between:
class Computer
{
public VGA VGAOutput { get; set; }
}
or
class Computer
{
public IVGA VGAOutput { get; set; }
}
I read somewhere that using interfaces is better, but why?
With abstract classes I can define an interface as well plus add data-members so why are interfaces the preferred method?
Does binary replacement play a role here as well?
thank you
Chris
You can inherit from(that is, implement) multiple interfaces. You can't inherit from multiple abstract classes
Check out this post:
http://www.codeproject.com/KB/cs/abstractsvsinterfaces.aspx
Most important features I see:
Multiple inheritance (interface can implement multiple interfaces, abstract can inherit from only one)
Homogeneity (I have two objects with the same interface, but they're really not they same object and shouldn't share any code)
Related
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.
Starting off, I'm working with EF, since I'm building an MVC application on C#. I want different types of exams to have different types of questions. Here are my abstract classes:
public abstract class Exam
{
public int Id { get; set; }
public string Description { set; get; }
public abstract ICollection<Question> GetQuestions();
public abstract void SetQuestions(ICollection<Question> questions);
}
public abstract class Question
{
public int Id { get; set; }
public string Description { set; get; }
public abstract Exam getExam();
public abstract void setExam(Exam exam);
}
Notice that instead of the typical public virtual ICollection<Question> in the Exam class declaration, I created an abstract setter and getter. So is the case for the Exam property in the Question class.
Here are my concrete Exam classes:
[Table("SingleExam")]
public class SingleExam : Exam
{
public virtual ICollection<SingleQuestion> Questions { get; set; }
public override ICollection<Question> GetQuestions() { return Questions as ICollection<Question>; }
public override void SetQuestions(ICollection<Question> questions)
{
if (!(questions is ICollection<SingleQuestion>))
throw new ArgumentException("You must set single questions.");
Questions = questions as ICollection<SingleQuestion>;
}
}
[Table("MultipleExam")]
public class MultipleExam : Exam
{
public virtual ICollection<MultipleQuestion> Questions { get; set; }
public override ICollection<Question> GetQuestions() { return Questions as ICollection<Question>; }
public override void SetQuestions(ICollection<Question> questions)
{
if (!(questions is ICollection<MultipleQuestion>))
throw new ArgumentException("You must set multiple questions.");
Questions = questions as ICollection<MultipleQuestion>;
}
}
...And my concrete Question classes:
[Table("SingleQuestion")]
public class SingleQuestion : Question
{
public int ExamId { get; set; }
public virtual SingleExam Exam { get; set; }
public override Exam getExam() { return Exam; }
public override void setExam(Exam exam)
{
if (!(exam is SingleExam))
throw new ArgumentException("You must set a SingleExam");
Exam = exam as SingleExam;
}
}
[Table("MultipleQuestion")]
public class MultipleQuestion : Question
{
public int ExamId { get; set; }
public virtual MultipleExam Exam { get; set; }
public override Exam getExam() { return Exam; }
public override void setExam(Exam exam)
{
if (!(exam is MultipleExam))
throw new ArgumentException("You must set a MultipleExam");
Exam = exam as MultipleExam;
}
}
I did all this because a MultipleExam should only have MultipleQuestions, and a SingleExam should only have SingleQuestions, the same way that MultipleQuestion should have a MultipleExam and Single question should have a SingleExam.
Is there a better way to ensure that a subclass of a class 'A' contains or has a specific subclass of class 'B' (As is the case with my Exams and Questions), and having access to it through the abstract class without the abstract getters and setters?
As other have mentioned I think you are over complicating your problem.
However; your question is about type guarantees and I will try to answer that.
First the code:
public interface IExam<out T> where T:IQuestion {
int Id { get; set; }
string Description { set; get; }
IEnumerable<T> GetQuestions();
}
public interface IQuestion{
int Id { get; set; }
string Description { set; get; }
IExam<IQuestion> Exam { get; }
}
public class SingleQuestion:IQuestion {
public string Description { get; set; }
public int Id { get; set; }
IExam<IQuestion> IQuestion.Exam {
get { return Exam; }
}
public SingleExam Exam { get; set; }
}
public class SingleExam:IExam<SingleQuestion> {
public int Id { get; set; }
public string Description { get; set; }
private IEnumerable<SingleQuestion> _questions;
public IEnumerable<SingleQuestion> GetQuestions() {
return _questions;
}
public void SetQuestions(IEnumerable<SingleQuestion> questions) {
_questions = questions;
}
}
First of all we have replaced the abstract classes with interfaces.
This is required because we want to make IExam covariant on IQuestion and covariance can only be defined in an interface. This is also why we change to an IEnumerable for the collection.
Note we do not define the SetQuestions method in IExam in short this is because we can't. In long it is because that would make T contravarient as well as contravarient which would in turn lead to circumstances where type guarantees could not be made.
IQuestions is fairly straight forward no real changes here. You could, I suppose, leave it as an abstract type though.
Now the implementations:
In SingleQuestion we must explicitly implement Exam which expects an IExam then shadow it with a property that returns a SingleExam.
This allows us to return the most exact type of exam possible.
SingleQuestion sq = new SingleQuestion();
IQuestion q = sq; //Upcast
sq.Exam; //returns a SingleExam
q.Exam; //returns a IExam<IQuestion>
In SingleExam you can now set the questions and restrict it so that only SingleQuestions may be added.
As an aside it is now easier to see why SetQuestions cannot be defined in IExam. Consider the following:
SingleExam se = new SingleExam();
IExam<IQuestion> singleUpcast = se;
//What type of question can we set on singleUpcast?
All we know is that singleUpcast contains IQuestions but we can't just add IQuestions because singleUpcast is ultimately an instance of SingleExam which promised that only SingleQuestions could be set so it. In short it is not possible to know what types can be added to IExam without potentially breaking type guarantees
Consider the following two classes that implements a bunch of properties from an interface:
Interface code:
public interface ISample
{
int x;
string y;
}
Class 1:
public class SampleA: ISample
{
public int x { get; set; }
public string y { get; set; }
}
Class 2:
public class SampleB: ISample
{
public int x { get; set; }
[Decorated]
public string y { get; set; }
}
Only difference here being is that SampleB has one property decorated with an attribute.
This is highly simplified and the classes in question have many more properties but the main differences being one class has some properties decorated with attributes.
There will be situations in the future where further classes will be introduced that implement the ISample interface and feel as though these classes should probably inherit some common code maybe from an abstract class or something.
What would be a good approach to refactor this code?
Try this solution: all properties at Sample class will be virtual, if you want to decorate some of them at derrived classes with attributes, just override them.
public class Sample
{
public virtual int x { get; set; }
public virtual string y { get; set; }
}
public class SampleA : Sample
{
}
public class SampleB : Sample
{
[Decorated]
public override string y { get; set; }
}
I have the following two classes:
abstract class LogItem {
public String payload { get; set; }
public String serverId { get; set; }
public DateTime timeRecieved { get; set; }
}
class MyLogItem : LogItem
{
//No I want this to have to have the members from the abstract class above, as if it where an interface?
}
So in other words I am wanting a type if interface that can have definitions or variables which all classes that implement it have to have, but they could add more if they required ?
The above example builds, even if i dono add the members from the abstract class.
edit
Forget what I've said before. These are attributes, not methods. For them to be accessible on derived classes, you make them protected or public. The difference is that public members are visible to the world, while protected ones are visible to the class and subclasses.
Any class derived from your LogItem may have other variables.
abstract class LogItem {
public String payload { get; set; }
public String serverId { get; set; }
public DateTime timeRecieved { get; set; }
}
class MyLogItem : LogItem
{
//No I want this to have to have the members from the abstract class above, as if it where an interface?
private void TestMethod(){
String test = payload;
}
}
check out this post for more information
Your MyLogItem class can reference any of the above members directly. They are accessible
You may declare an interface with those
public interface MyInterface {
public String payload { get; set; }
public String serverId { get; set; }
public DateTime timeRecieved { get; set; }
}
and your class
public class MyLogItem : MyInterface
{
String _payload;
public String payload { get{ return _payload; } set {_payload=value;} }
...
}
The abstract keyword can also be applied to methods, as described here.
I have an application that has a concept of a Venue, a place where events happen. A Venue has many VenueParts. So, it looks like this:
public abstract class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<VenuePart> VenueParts { get; set; }
}
A Venue can be a GolfCourseVenue, which is a Venue that has a Slope and a specific kind of VenuePart called a HoleVenuePart:
public class GolfCourseVenue : Venue
{
public string Slope { get; set; }
public virtual ICollection<HoleVenuePart> Holes { get; set; }
}
In the future, there may also be other kinds of Venues that all inherit from Venue. They might add their own fields, and will always have VenueParts of their own specific type.
Here are the VenuePart classes:
public abstract class VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public override string NameDescriptor { get { return "Hole"; } }
public int Yardage { get; set; }
}
My declarations above seem wrong, because now I have a GolfCourseVenue with two collections, when really it should just have the one. I can't override it, because the type is different, right? When I run reports, I would like to refer to the classes generically, where I just spit out Venues and VenueParts. But, when I render forms and such, I would like to be specific.
I have a lot of relationships like this and am wondering what I am doing wrong. For example, I have an Order that has OrderItems, but also specific kinds of Orders that have specific kinds of OrderItems.
Update: I should note that these classes are Entity Framework Code-First entities. I was hoping this wouldn't matter, but I guess it might. I need to structure the classes in a way that Code-First can properly create tables. It doesn't look like Code-First can handle generics. Sorry this implementation detail is getting in the way of an elegant solution :/
Update 2: Someone linked to a search that pointed at Covariance and Contravariance, which seemed to be a way to constrain lists within subtypes to be of a given subtype themselves. That seems really promising, but the person deleted their answer! Does anyone have any information on how I may leverage these concepts?
Update 3: Removed the navigation properties that were in child objects, because it was confusing people and not helping to describe the problem.
Here's one possible option using generics:
public abstract class VenuePart
{
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public string NameDescriptor { get{return "I'm a hole venue"; } }
}
public class Venue<T> where T : VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<T> VenueParts { get; set; }
}
public class GolfCourseVenue : Venue<HoleVenuePart>
{
}
Here GolfCourseVenue has the collection VenueParts, which can contain HoleVenueParts or super classes HoleVenueParts. Other specializations of Venue would restrict VenueParts to containing VenueParts specific to that venue.
A second possibility is pretty much as you had it
public abstract class VenuePart
{
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public string NameDescriptor { get{return "I'm a hole venue"; } }
}
public class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<VenuePart> VenueParts { get; set; }
}
public class GolfCourseVenue : Venue
{
}
Now GolfCourseVenue has the collection VenueParts, which can contain VenueParts or super classes VenueParts. Here all specializations of Venue can contain any type of VenuePart which may or may not be appropriate.
In answer to your comment about covariance, I would propose something like this:
public abstract class VenuePart
{
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public override string NameDescriptor { get{return "I'm a hole venue"; } }
}
public abstract class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public abstract ICollection<VenuePart> VenueParts { get; }
}
public class GolfCourseVenue : Venue
{
private ICollection<HoleVenuePart> _holeVenueParts;
public GolfCourseVenue(ICollection<HoleVenuePart> parts)
{
_holeVenueParts = parts;
}
public override ICollection<VenuePart> VenueParts
{
get
{
// Here we need to prevent clients adding
// new VenuePart to the VenueParts collection.
// They have to use Add(HoleVenuePart part).
// Unfortunately only interfaces are covariant not types.
return new ReadOnlyCollection<VenuePart>(
_holeVenueParts.OfType<VenuePart>().ToList());
}
}
public void Add(HoleVenuePart part) { _holeVenueParts.Add(part); }
}
I look forward to the advice of others - but my approach is to use generics in this case. With generics, your GolfCourseVenue's "parts" are strong typed!
...and as I type this everyone else is saying generics too. HOW DO YOU overstackers type so dang fast?!
Anyways, pretending I'm still first -
public class VenuePart
{
}
public class HoleVenuePart : VenuePart
{
}
public abstract class Venue<T> where T : VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<T> Parts { get; set; }
}
public class GolfCourseVenue : Venue<HoleVenuePart>
{
public string Slope { get; set; }
}
Also, as a 2nd option, you could use an interface too, so in case you didn't like the name Parts, you could call it Holes when the derived type is known to be a GolfCourse
public class VenuePart
{
}
public class HoleVenuePart : VenuePart
{
}
public interface IPartCollection<T> where T : VenuePart
{
ICollection<T> Parts { get; set; }
}
public abstract class Venue<T> : IPartCollection<T> where T : VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<T> Parts { get; set; }
}
public class GolfCourseVenue : Venue<HoleVenuePart>
{
public string Slope { get; set; }
ICollection<HoleVenuePart> IPartCollection<HoleVenuePart>.Parts { get { return base.Parts; } set { base.Parts = value; }}
public virtual ICollection<HoleVenuePart> Holes { get { return base.Parts; } set { base.Parts = value;}}
}
You can use Covariance
public abstract class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual IEnumerable<VenuePart> VenueParts { get; set; }
}
public class GolfCourseVenue : Venue
{
public string Slope { get; set; }
public GolfCourseVenue()
{
List<HoleVenuePart> HoleVenueParts = new List<HoleVenuePart>();
HoleVenueParts.Add(new HoleVenuePart());
VenueParts = HoleVenueParts;
}
}
Assuming HoleVenuePart is inherited from VenuePart
If you remove "set" portions of both collections than it will make more sense: base class provides "all parts" collection, while derived classes have filtered view in addition to base class one.
Note: Depending on your needs making GolfVenue to be specialization generic of Venue<VenuePart> may not work as Venue<Type1> and Venue<Type2> will not have any good base class to work with.
Consider using interfaces instead of base classes as it would allow more flexibility in implementation.
public interface IVenue
{
public int Id { get; }
public string Name { get; }
public virtual IEnumerabe<VenuePart> VenueParts { get; }
}
public interface IGolfCourse : IVenue
{
public virtual IEnumerabe<HoleVenuePart> Holes { get; }
}
Now you can use GolfCourse:Venue from other samples but since it implements interface you can handle it in gnereic way too:
class GolfCourse:Venue<HoleVenuePart>, IGolfCourse {
public virtual IEnumerabe<VenuePart> Holes{ get
{
return VenueParts.OfType<HoleVenuePart>();
}
}
}
class OtherPlace:Venue<VenuePart>, IVenue {...}
List<IVenue> = new List<IVenue> { new GolfCourse(), new OtherPlace() };
Nothe that GolfCourse and OtherPlace don't have common parent class (except object), so without interface you can't use them interchangebly.