Combing interfaces of derived classes [duplicate] - c#

This question already has answers here:
Convert List<DerivedClass> to List<BaseClass>
(13 answers)
Closed 5 years ago.
I have classes which implement interfaces of classes derived from a common base. Is there any way that I can combine these to work with them as a set?
I have been exploring co and contravariance but without success.
Thanks for your help.
void Main()
{
var textAnswers = new IAnswerValidator<TextQuestion, TextAnswer>[] { new NoDogsValidator(), new MaxLengthValidator() };
var dateAnswers = new IAnswerValidator<DateQuestion, DateAnswer>[] { new NotChristmasDayValidator() };
// Can I combine into a list or enumerable?
// var allValidators = new List<IAnswerValidator<QuestionBase, AnswerBase>>();
// allValidators.AddRange(textAnswers);
// allValidators.AddRange(dateAnswers);
// The goal is to be able to combine so as to be able to work on them as a set.
}
public class ValidationResult { }
public class AnswerBase { }
public class TextAnswer : AnswerBase { }
public class DateAnswer : AnswerBase { }
public class QuestionBase { }
public class TextQuestion : QuestionBase { }
public class DateQuestion : QuestionBase { }
public interface IAnswerValidator<TQuestion, TAnswer> where TQuestion : QuestionBase, new() where TAnswer : AnswerBase, new()
{
ValidationResult Validate(TQuestion question, TAnswer answer);
}
public class NoDogsValidator : IAnswerValidator<TextQuestion, TextAnswer>
{
public ValidationResult Validate(TextQuestion question, TextAnswer answer) { return new ValidationResult(); } // simplified
}
public class MaxLengthValidator : IAnswerValidator<TextQuestion, TextAnswer>
{
public ValidationResult Validate(TextQuestion question, TextAnswer answer) { return new ValidationResult(); } // simplified
}
public class NotChristmasDayValidator : IAnswerValidator<DateQuestion, DateAnswer>
{
public ValidationResult Validate(DateQuestion question, DateAnswer answer) { return new ValidationResult(); } // simplified
}

Is there any way that I can combine these to work with them as a set?
Not and keep type-safety.
For example, consider your proposed code:
var allValidators = new List<IAnswerValidator<QuestionBase, AnswerBase>>();
allValidators.AddRange(textAnswers);
allValidators.AddRange(dateAnswers);
Suppose the compiler let you do that. Then what do you say should happen if you do something like this:
QuestionBase question = new TextQuestion();
AnswerBase answer = new TextAnswer();
foreach (var validator in allValidators)
{
validator.Validate(question, answer);
}
In particular, when it gets to the NotChristmasDayValidator element in the list, what's that object going to do when you pass its Validate() method objects that are not DateQuestion and DateAnswer, respectively?
Your goal is fundamentally broken. You say you want to combine all the objects into a single list, but you haven't explained why that's useful nor what you think you'd be able to do with such a list. There are of course ways you can put all those objects into the same list, but only by discarding the type safety. For example, just make your allValidators object a List<object> instead. Then you can put whatever you want in the list. But you'll have to do extra type-checking later, when using the list.
Until if and when you're able to explain a design goal that is safe and sensible, all we can say for now is "no, you can't do that, it's not safe".

Related

Given a custom generic class that stores a List<T> how do I prevent adding an object of type T more than once to the List<T>? [duplicate]

This question already has answers here:
What does Collection.Contains() use to check for existing objects?
(6 answers)
Closed 1 year ago.
So this is the code that I have tried, but it adds the same object more than once:
namespace TestComparison
{
public interface IAddable
{
int RandomIntValue { get; set; } // often Times this value will repeat.
}
public class AdditionManager<T> where T : IAddable
{
private List<T> addables;
public AdditionManager()
{
addables = new List<T>();
}
public void Add(T _addable)
{
if (!addables.Contains(_addable))
{
addables.Add(_addable);
}
}
}
public class TestAddable : IAddable
{
public int RandomIntValue { get; set; }
public Data UniqueData = new Data() { UniqueId = 10023 }; // This is what really make each item unique
}
public class Data
{
public int UniqueId { get; set; }
}
}
I've heard about the IEqualityComparer and I have implemented it in non-generic classes, but I'm not quite sure how to implement it here.
Your problem indeed seems to be related to a missing IEqualityComparer.
Imagine the following:
class TestClass
{
public int x;
}
class Program
{
static void Main(string[] args)
{
TestClass nine = new TestClass() { x = 9 };
TestClass twelve = new TestClass() { x = 12 };
TestClass anotherNine = new TestClass() { x = 9 };
Console.WriteLine(nine == twelve);
Console.WriteLine(nine == anotherNine);
}
}
What will this program output? The "surprising" answer is that it outputs False two times. This is because the objects are compared to each other, not the members of the objects. To achieve an actual value comparison which will compare the objects by their content instead of their reference you need to consider quite a few things. If you want to be really complete, you need IComparable, IEquality, GetHashcode etc etc. It's very easy to make a mistake there.
But since C# 9.0 there's a new type which can be used instead of class. The type is record. This new record type has all the stuff I mentioned implemented by default. If you want to go the long route, I suggest you to look into the new record type and what it actually is.
This means all you need to do is change the type of your TestAddable and Data from class to record and you should be fine.
You can use dependency injection to provide you with generic implementation. Doing so you'll need to provide the custom IEqualityComparer<T> implementation that you want at the point of generic object's construction.
public class AdditionManager<T> where T : IAddable
{
private List<T> addables;
private IEqualityComparer<T> comparer;
public AdditionManager()
: this (EqualityComparer<T>.Default)
{ }
public AdditionManager(IEqualityComparer<T> _comparer)
{
addables = new List<T>();
comparer = _comparer;
}
public void Add(T _addable)
{
if (!addables.Contains(_addable, comparer))
{
addables.Add(_addable);
}
}
}
However, if you are looking for you list of addables to be unique based on some constraint, I would not use the above implementation for performance reasons. As the List<T>.Contains check will become slower as the list grows larger.
If the order of the list does not matter change your List<T> to a HashSet<T>. HashSet<T>.Contains will be just as quick as a Dictionary<TKey, TValue> lookup. But this call can be avoided altogether with HashSet<T> as the Add call will first check to see if the item is in the set before adding it, and return true or false to indicate it was added or not`
So if the order of addables is of not concern, then I would use the following implementation.
public class AdditionManager<T> where T : IAddable
{
private HashSet<T> addables;
public AdditionManager()
: this(EqualityComparer<T>.Default)
{ }
public AdditionManager(IEqualityComparer<T> _comparer)
{
addables = new HashSet<T>(_comparer);
}
public void Add(T _addable)
{
// will not add the item to the HashSet if it is already present
addables.Add(_addable);
}
}
If you need to maintain the order of addables then I suggest maintaining the list of objects in both a HashSet<T> and List<T>. This will provide you with the performance of the above implementation, but maintain the addition order on your items. In this implementation any of the operations you need to perform, do them against the List<T> and only use the HashSet<T> to make sure the item isn't already present when adding to List<T> If you are going to have some type of Remove operation, make sure to remove the item from both the HashSet<T> and List<T>
public class AdditionManager<T> where T : IAddable
{
private HashSet<T> set;
private List<T> list;
public AdditionManager()
: this(EqualityComparer<T>.Default)
{ }
public AdditionManager(IEqualityComparer<T> _comparer)
{
set = new HashSet<T>(_comparer);
list = new List<T>();
}
public void Add(T _addable)
{
if (set.Add(_addable))
list.Add(_addable);
}
}
To create this object using TestAddable you'll need an IEqualityComparer<TestAddable> like the following. As others have suggested, the field(s) you are doing your comparison on should be made immutable, as a mutable key is going to cause bugs.
public class TestAddableComparer : IEqualityComparer<TestAddable>
{
public bool Equals(TestAddable x, TestAddable y)
{
return x.UniqueData.Equals(y.UniqueData);
}
public int GetHashCode(TestAddable obj)
{
// since you are only comparing use `UniqueData` use that here for the hash code
return obj.UniqueData.GetHashCode();
}
}
Then to create the manager object do:
var additionManager = new AdditionManager<TestAddable>(new TestAddableComparer());
You can use a dictionary instead of a list. If you need a list in other parts of your code, it is easy to add a property that exposes the Values only.
public class AdditionManager<T> where T : IAddable
{
private Dictionary<int,T> addables;
public AdditionManager()
{
addables = new Dictionary<int,T>();
}
public void Add(T _addable)
{
if (!addables.ContainsKey(_addable.Data.RandomIntValue))
{
addables.Add(_addable.Data.RandomIntValue, _addable);
}
}
public Dictionary<int,T>.ValueCollection Values => _addables.Values;
}

How to make a dictionary of derived classes in c#? [duplicate]

This question already has answers here:
Convert List<DerivedClass> to List<BaseClass>
(13 answers)
Closed 3 years ago.
I am trying to make a dictionary of lists of derived objects. My solution was to use classes for each object that implemented the same base interface. The issue is that interface must have the property of the list of the object its designated to contain. That property must allow me to store each one of the derived classes without data loss from the objects.
I've tried down casting the lists and putting them straight into the dictionaries. I've tried making wrapper classes and down casting the base property to the proper list also.
public class Body
{
Dictionary<string, List<BodyPart>> parts;
public Body(List<Arms> arms_, List<Head> head_) //etc
{
parts = new Dictionary<string, List<BodyPart>>()
{
{"arms", arms_},
{"head", head_}
//etc
}
}
}
problem with this solution is that the lists of specific derived body parts will not cast to a list of the base class BodyPart. The other issue is that I'm also certain that because this is down casting it will cause data loss as I will only be able to reference the objects as the base class.
I expect the result to be a dictionary of different body parts that I can reference without data loss.
You must convert the lists
Dictionary<string, List<BodyPart>> parts;
public Body(List<Arms> arms_, List<Head> head_) //etc
{
parts = new Dictionary<string, List<BodyPart>>()
{
{"arms", new List<BodyPart>(arms_)},
{"head", new List<BodyPart>(head_)}
//etc
}
}
The reason is this: assume that you could insert a List<Head> into the dictionary. Then you retrieve it with
List<BodyPart> bodyParts = parts["head"];
and add it arms
bodyParts.Add(new Arms()); // Oops! this is a List<Head>.
Therefore a List<Derived> is not assignment compatible to a List<Parent>. Maybe it would just be simpler to type all these lists as List<BodyPart>, even when they contain Arms and Heads.
This new List<BodyPart>(head_) woks, because the constructor accepts an IEnumerable<BodyPart> as parameter. An IEnumerable<T> is read-only. Therefore the list (which implements IEnumerable<Head>) you pass as argument will only be read and its elements will be added to a List<BodyPart>, which is okay.
See: Covariance and Contravariance (C#)
Try something like this :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<BodyPart> arms = new List<BodyPart>() { new BodyPart() { name = "Left Arm" }, new BodyPart() { name = "Right Arm" } };
List<BodyPart> head = new List<BodyPart>() { new BodyPart() { name = "Head" } };
new Body(arms, head);
}
}
public class Body
{
Dictionary<string, List<BodyPart>> parts = new Dictionary<string, List<BodyPart>>();
public Body(List<BodyPart> arms_, List<BodyPart> head_) //etc
{
parts.Add("arms", arms_);
parts.Add("head", head_);
}
}
public class BodyPart
{
public string name { get; set; }
}
}

How to access member variable of derived class from an abstract list in C# [duplicate]

This question already has answers here:
When to use Cast() and Oftype() in Linq
(7 answers)
Closed 4 years ago.
this is the code for the base class:
public abstract class baseClass
{
public string Name { get; set; }
}
then there are two derived classes
public derived1: baseClass
{
public int derivedMemberVariable { get; set; }
}
public derived2: baseClass
{
public string derivedMemberVariableAlternative { get; set; }
}
I then created a list of the base class and add both derived types to it:
List<baseClass> baseList = new List<baseClass>();
derived1 der1 = new derived1{
Name = "Derived1 Name",
derivedMemberVariable = 10
};
derived2 der2 = new derived2{
Name = "Derived2 Name",
derivedMemberVariable = "STRING"
};
baseList.Add(der1);
baseList.Add(der2);
foreach (b in baseList){
b.derivedMemberVariable //this is what fails
}
how can I access this derived member variable from the list created with the base class??
I have to use the code the way it is because it is for a school project so I cant modify the way the classes work but I think I can modify the list? but the list needs to contain both of the derived classes.
am I going to be able to accomplish this or is there a different approach id have to take?
ok, so for some reason as soon as I tried to cast this time it worked! I feel dumb :/
thanks to AntiTcb for pointing it out.
what I ended up doing was:
foreach (b in baseList){
if(b.GetType() == der1.GetType()){
var q = b as derived1;
q.derivedMemberVariable; // now works
}
if(b.GetType() == der2.GetType()){
var q = b as derived2;
q.derivedMemberVariableAlternative; // now works
}
}
Use pattern matching:
foreach (b in baseList){
{
if(b is DerivedType d)
{
d.derivedMemberVariable //this is what fails
}
}

Generic class to store variable content

I want to create a structure to store data consumed from a Web Service with the followind specs:
Response:
Field 1 - InstructionType: Can be 1 (PreferredDay), 2 (SVP), 3 (Neighbour)
Field 2: Some variable data. Its type depends on Field 1. So if:
Field 1 == 1 then Field 2 type will be of DateTime (dd.MM.yyyy)
Field 1 == 2 then Field 2 type will be of type string.
Field 1 == 3 then Field 2 type will be of type string
So, I started up with the following enum:
public enum InstructionType
{
None = 0,
PreferredDay = 1,
ServicePoint = 2,
Neighbour = 3
}
And the generic class:
public abstract class Instruction<T>
{
public InstructionType Type { get; private set; }
public T Data { get; private set; }
public Instruction(InstructionType type, T data)
{
this.Type = type;
this.Data = data;
}
}
and concrete classes:
public class PreferredDayInstruction : Instruction<DateTime>
{
public PreferredDayInstruction(DateTime data)
: base (InstructionType.PreferredDay, data) {}
}
public class ServicePointInstruction: Instruction<string>
{
public ServicePointInstruction(string data)
: base (InstructionType.ServicePoint, data) {}
}
public class NeughbourInstruction: Instruction<string>
{
public NeughbourInstruction(string data)
: base (InstructionType.Neighbour, data) {}
}
When parsing web service's response created a public function:
public Instruction DeliveryInstruction() <---Compiler error here "Instruction"
{
if (resultFromWebservice.Field1 == 1)
return new PreferredDayInstruction((DateTime)Field2);
if (resultFromWebservice.Field1 == 2)
return new ServicePointInstruction(Field2);
if (resultFromWebservice.Field1 == 3)
return new NeighbourInstruction(Field2);
}
and here is the problem. Can't return objects of generic type.
Tried with with Interface, factories, and other stuff, but allways with the same problem. So, is there any way to archieve this? maybe it's not possible or maybe is so easy I can't see now. Thanks in advance.
UPDATE:
Compiler error on BOLD Instruction
Error 1 Using the generic type 'NAMESPACE.Instruction' requires '1' type arguments
I forgot..I'm using .NET 3.5
It looks like you may be starting off with an intent to use generics rather than using them because you've identified a need. Often (not always) when that gets difficult it's because it didn't actually fit what you were trying to do.
What seems odd in this case is that you have both a generic type and an enum to indicate the type. This is likely to cause you a few problems.
First it looks like you're trying to create a one-size-fits all class to model different types of behaviors. That will start off confusing and get more confusing. Think of most classes that are part of the .NET framework, and imagine what would happen if they had properties like Field1 and Field2, and you couldn't tell from looking at them what they were for. And in one method they're used for one thing, but in a another case they mean something else.
Also, if you're trying to put different types of instructions in one class, that suggests that maybe you're going to try passing them all to one method, and that method figures out what to do, and maybe calls other methods. (I'm guessing that because of the enum. Perhaps you're going to handle the input differently depending on which value it contains.) That one method will get really hard to maintain.
I'd recommend waiting on generics until you're sure you need them. And if you have different types of instructions you're likely better off writing a different class for each one with the properties it needs and names that describe them, and writing methods for each of them to do what they need to do. If you need lots of classes, make lots of them.
It's very easy to fall into the trap of trying to solve problems that don't exist, like how do I write one class that covers a bunch of different needs. The answer usually that you don't need to. You'll get better results from writing more classes that each do fewer things.
Believe me that I tried to do my best to explain what was my problem and what I needed in order to solve it. In a nutshell, the question was quite simple. Is this possible or not? So, is there a way to return a common type for these 3 classes? Answer is no, as they don't share any root. They all derive from Instruction, but aren't compatible each other. That's what I learned from this experience.
As another example, lets take another .NET framework's generic type.
public class ListOfString : List<string> { }
public class ListOfInt : List<int> { }
public class ListOfDecimal : List<decimal> { }
And, in another place of the application, get a method who returns one of this List based on some logic:
public class Logic
{
public List<> GetList(Type t) <----This can't be done
{
if (t == typeof(string))
return new ListOfString();
if (t == typeof(int))
return new ListOfInt();
if (t == typeof(decimal))
return new ListOfDecimal();
else return null;
}
}
Please, keep in mind that this is just a stupid sample just to show what's the point of this post.
By the way, in the case of List the following can be done, because there is a non generic different version of IList:
public IList GetList(Type t)
{
....
}
But I can't think of a way to do this in my particular case.
Anyway, I finally followed another approach. I reallized that what I really wanted is to ensure Data property is valid. If it it's supposed to be a date there, ensure date is valid. Is it a string, ensure it has the right length or whatever rule it must follow.
So this is the final solution:
The enum:
public enum InstructionType
{
None = 0,
PreferredDay = 1,
ServicePoint = 2,
Neighbour = 3
}
The base class:
public abstract class Instruction
{
public InstructionType Type { get; private set; }
public string Data { get; private set; } <---Type String
public Instruction(InstructionType type, string data)
{
this.Type = type;
this.Data = IsValid(data) ? data : string.Empty;
}
public abstract bool IsValid(string data); <--the rule.
}
The concrete classes:
public class PreferredDayInstruction : Instruction
{
public PreferredDayInstruction(string date)
: base(InstructionType.PreferredDay, date) { }
public override bool IsValid(string data)
{
string[] formats = {"dd.MM.yyyy", "d.MM.yyyy",
"dd.MM.yy", "d.MM.yy"};
try
{
data = data.Replace('/', '.').Replace('-', '.');
var dateparts = data.Split('.');
DateTime date = new DateTime(Convert.ToInt32(dateparts[2]),
Convert.ToInt32(dateparts[1]),
Convert.ToInt32(dateparts[0]));
//DateTime.ParseExact(data, formats, null, System.Globalization.DateTimeStyles.AssumeLocal);
return true;
}
catch (Exception)
{
return false;
}
}
}
public class ServicePointInstruction : Instruction
{
public ServicePointInstruction(string data)
: base (InstructionType.ServicePoint, data) { }
public override bool IsValid(string data)
{
return ServicePointBarcodeValidator.Validate(data);
}
}
public class NeighbourInstruction : Instruction
{
public NeighbourInstruction(string data) :
base(InstructionType.Neighbour, data) { }
public override bool IsValid(string data)
{
return data.Length <= 70;
}
}
A factory class, who's responsability is to create and return the correct object based on the enum:
public static class DeliveryInstructionFactory
{
public static Instruction Create(int type, string data)
{
return Create((InstructionType)type, data);
}
public static Instruction Create(InstructionType type, string data)
{
switch (type)
{
case InstructionType.PreferredDay:
return new PreferredDayInstruction(data);
case InstructionType.ServicePoint:
return new ServicePointInstruction(data);
case InstructionType.Neighbour:
return new NeighbourInstruction(data);
default:
return null;
}
}
}
And finally, as now all of they share the same root, object can be created on webservice's response parser:
public Instruction DeliveryInstruction()
{
try
{
int instructionCode = int.Parse(observation.Substring(173,2));
string instructionData = observation.Substring(175, 10);
return DeliveryInstructionFactory.Create(instructionCode, instructionData); }
catch (Exception ex)
{
Log.Error("[ValidationBarcodeResponse] DeliveryInstructions aren't in the correct format", ex);
return null;
}
}
Hope this now fits on a Minimal, Complete, and Verifiable example

Using reflection to get static property value, a concatenation of derived and base class

I'm going to do my best to explain my vision here. This is a very lame made-up example. I've got a few different types of Bags, and they all hold their own special type of Marble. Each type of Marble has its own set of Nicknames (strings).
Unfortunately, there are other things besides the Marble in the Bag, so generics won't help me here.
// Bags
abstract class Bag {
protected Type MarbleType { get; }
protected List<Marble> _marbles;
public void DumpBag()
{ ... }
}
class RedBag : Bag {
override Type MarbleType { get { return typeof(RedMarble); } }
}
class BlueBag : Bag {
override Type MarbleType { get { return typeof(BlueMarble); } }
}
// Marbles
abstract class Marble {
public static IEnumerable<string> Nicknames {
get {
return new List<string>() {
"Marble", "RollyThing"
}
}
}
}
class RedMarble : Marble {
public static IEnumerable<string> Nicknames {
get {
return new List<string>(Marble.Nicknames) {
"Ruby"
};
}
}
}
class BlueMarble : Marble { ... }
So now we get to the details, the implementation of DumpBag(). Consider the following call:
Bag b = new RedBag();
b.GetMarbles();
b.DumpBag();
I would like it to print:
Bag of Marbles (aka "Marble", "RollyThing", Ruby"):
- Marble 1
- Marble 2
...
We see that, in order to print that heading, the Bag must be able to have knowledge of the derived type of Marble, independent of any actual instances. It gets a concatenation of the Nicknames of the Marble base class, but also the derived RedMarble.
DumpBag needs to do a kind of 'static virtual call'. I've started implementing DumpBag with the following:
public void DumpBag() {
PropertyInfo pi = this.MarbleType.GetProperty("Nicknames", BindingFlags.Static);
IEnumerable<string> nicknames = pi.GetValue(null, null); // No instance
StringBuilder sb = new StringBuilder("Bag of Marbles (aka ");
foreach (string nn in nicknames)
sb.Append("\"" + nn + "\", ");
Console.WriteLine(sb.ToString());
...
}
My questions:
Is this sane? Hopefully I have (or I can) explain my rationale for why I've gone this route.
I get a warning (of course) that RedMarble.Nicknames hides Marble.Nicknames. Does it seem valid to go ahead and mark it new?
You'll find all you're missing is an explicit cast:
(List<string>)this.MarbleType.GetProperty("Nicknames").GetValue(null, null);
This worked fine for me when I tested it.
And as discussed in the comments, no you shouldn't be using the new keyword really, you're better off naming the base class static method to something else so there is no ambiguity. You are after all in control of this and not using someone else's code.
Now, should you do it this way?
Well, first surely you want to use generics not defined methods to return types:
abstract class Bag<T> where T:marble {
public void DumpBag()
{
// here you can use
// (IEnumerable<string>)typeof(T).GetProperty("Nicknames").GetValue(null, null);
}
}
class RedBag : Bag<RedMarble> {
}
class BlueBag : Bag<BlueMarble> {
}
Of course the second thing you could do is make this not static, in which case the property will be abstract in Marble, and overridden in RedMarble and BlueMarble, and then just accessed in DumpBag directly as Nicknames rather than using reflection.

Categories