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; }
}
}
Related
I'm trying to convert a collection of multiple types (using a generic class that implements an interface to enable storage) into XML.
That's the short explanation of what I'm trying to do. Below is a description of what I've tried thus far and where I'm currently stuck.
The current implementation is a dictionary that can take a string as a key and any type (stored as the interface) as a value. (Technically I've got a parallel array structure going on right now because that's easier to serialize than a dictionary but for the below example I've used a dictionary cause it reduces some guff so just pretend it works ;) )
Sadly, you cannot serialze interfaces.
I know IXmlSerializable exists but I get this error when I try having that as the value:
InvalidOperationException: System.Xml.Serialization.IXmlSerializable cannot be serialized because it does not have a parameterless constructor.
Rethrow as InvalidOperationException: Cannot serialize member 'GenericDictionary.A_values' of type 'System.Xml.Serialization.IXmlSerializable[]', see inner exception for more details.
(A_values is a IXmlSerializable Array, also am happy to post the inner explaination but it's a bit long)
Now I know that IXmlSerializable is supposed to be implemented in a class and then you would stored that class in your collection, however I cannot store the class in the dictionary because it requires a type and that's what I'm trying to avoid.
Any guidance would be useful.
Here's more or less the code
using System;
using System.Xml;
using System.Xml.Serialization;
[XmlRoot("GenericDictionary")]
public class GenericDictionary
{
[XmlArray("parameters")]
private Dictionary<string, IGenericParameter> genericDictionary = new Dictionary<string, IGenericParameter>();
public GenericDictionary(){}
public T GetParameter<T>(string _key)
{
// Cast the interface to a concrete implementation and access the parameter
GenericParameter paramToGet = (GenericParameter)genericDictionary[_key];
return paramToGet.GetParam;
}
public void AddParameter<T>(string _key, T _value)
{
// Add a new GenericParameter (which will be stored as its interface but still accessible via casting)
genericDictionary.Add(_key, new GenericParameter(_value));
}
}
// This exists to allow us to store GenericParameter in the dictionary without a type passed in
public interface IGenericParameter{}
[XmlRoot("parameter")]
public class GenericParameter<T> : IGenericParameter
{
[XmlAttribute("value")]
private T _param;
public T GetParam { get { return _param; } }
// XML Serializer needs an empty constructor
public GenericParameter(){}
public GenericParameter(T _value)
{
_param = _value;
}
}
public class Writer
{
GenericDictionary testDictionary;
public void TestWrite(string _filePath)
{
testDictionary = new GenericDictionary();
testDictionary.AddParameter<float>("TestFloat", 0.5f);
testDictionary.AddParameter<int>("TestInt", 16);
XmlSerializer serializer = new XmlSerializer(typeof(GenericDictionary));
FileStream fs = new FileStream(_filePath, FileMode.Create);
serializer.Serialize(fs, testDictionary);
fs.close();
}
}
This question already has answers here:
Convert List<DerivedClass> to List<BaseClass>
(13 answers)
Closed 3 years ago.
I have a class that have a property (SearchResults) that need to be List<T>, where T is one of my search classes depend on a condition
public class SearchGeneralResponse : ActionResponse
{
public IList<ISearchRecord> SearchResults { get; set; }
public SearchGeneralResponse(MbsObjectType searchType)
{
if(searchType == MbsObjectType.SourceRepo) SearchResults = new List<SearchRecord>();
if(searchType == MbsObjectType.BuildConfiguration) SearchResults = new List<SearchRecordBuild>();
}
}
I can cast new SearchRecord to ISearchRecord. But when I do it with list
this.SearchResults = new List<SearchRecord>();
I get this error:
Cannot implicitly convert type System.Collections.Generic.List 'SearchRecord' to System.Collections.Generic.List 'ISearchRecord'
Here's my interface:
public interface ISearchRecord
{
}
And one of the derived classes:
public class SearchRecord : ISearchRecord
{
public string Name { get; set; }
public string Summary { get; set; }
}
How can I create a List<T> property that can be initialized to a list of a class depending on a certain condition?
Add a cast to your initializer:
this.SearchResults = new List<SearchRecord>().Cast<ISearchRecord>().ToList();
Generics provide compile-time type safety, but in this case you're trying to tell the compiler that it should just trust you that there won't be run-time problems. Its job is to not trust you :)
Consider what would happen if this assignment were allowed and you did this:
IList<ISearchRecord> results; // results is of type IList<ISearchRecord>
results = new List<SearchRecord>(); // but it holds a value of type List<SearchRecord>
results.Add(new SomeOtherSearchRecord()); // ERROR
Since the property SearchResults is of type IList<ISearchRecord>, any code which uses that property can assign any implementation of ISearchRecord to an element of that list. So it needs to always be ISearchRecord and not a more specific implementing type.
Step back and consider the semantics of what your code needs to do. Should SearchResults support any implementation of ISearchRecord? If it's always going to be assigned from SearchRecord then make it that specific type:
public IList<SearchRecord> SearchResults { get; set; }
Or, if it needs to be a list of ISearchRecord (so it can support other implementations) then you'd have to create the list of that type:
this.SearchResults = new List<ISearchRecord>();
Edit: Also, if your new List<>() is just a contrived example and you're actually getting the list from somewhere else, you still need to create a new list. Fortunately the references within that list can still be to the same objects, but the list itself needs to be the correct compile-time type. You could achieve this with:
this.SearchResults = someOtherList.Cast<ISearchRecord>().ToList();
This would create a new list object, of the correct type, containing the same elements as someOtherList.
This question already has answers here:
IEnumerable vs IReadonlyCollection vs ReadonlyCollection for exposing a list member
(5 answers)
Closed 3 years ago.
Let's have this class:
ExampleClass
{
public List<string> MyValue { get; set; }
}
The question is how to restrict outside classes to modify of that property, means add object to collection, make new().
you can have something like this
public ReadOnlyCollection<string> MyValue {get; private set;}
You could expose it as IEnumerable<string> instead of as a list. This interface will not allow adds. You can still store it as a list internally, as a private field, so that the class itself can add or remove if needed.
For example:
class ExampleClass
{
private List<string> _myValue = new List<string>();
public IEnumerable<string> MyValue
{
get
{
foreach (var s in _myValue) yield return s;
}
}
}
If the caller would like to work with its own list, it can of course do this:
var list = exampleClass.MyValue.ToList();
At which point the caller owns it and it is clear that anything it chooses to add has nothing to do with the original list.
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".
I have just started using Newtonsoft.Json (Json.net). In my first simple test, I ran into a problem when deserializing generic lists. In my code sample below I serialize an object, containing three types of simple integer lists (property, member var and array).
The resulting json looks fine (the lists are converted into json-arrays). However, when I deserialize the json back to a new object of the same type, all list items are duplicated, expect for the array. I've illustrated that by serializing it a second time.
From searching around, I've read that there may be a "private" backing field to the lists that the deserializer also fills.
So my question is: Is there a (preferably simple) way to avoid duplicate items in following case?
Code
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace JsonSerializeExample
{
public class Program
{
static void Main()
{
var data = new SomeData();
var json = JsonConvert.SerializeObject(data);
Console.WriteLine("First : {0}", json);
var data2 = JsonConvert.DeserializeObject<SomeData>(json);
var json2 = JsonConvert.SerializeObject(data2);
Console.WriteLine("Second: {0}", json2);
}
}
public class SomeData
{
public string SimpleField;
public int[] IntArray;
public IList<int> IntListProperty { get; set; }
public IList<int> IntListMember;
public SomeData()
{
SimpleField = "Some data";
IntArray = new[] { 7, 8, 9 };
IntListProperty = new List<int> { 1, 2, 3 };
IntListMember = new List<int> { 4, 5, 6 };
}
}
}
Resulting output
First : {"SimpleField":"Some data","IntArray":[7,8,9],"IntListMember":[4,5,6],"IntListProperty":[1,2,3]}
Second: {"SimpleField":"Some data","IntArray":[7,8,9],"IntListMember":[4,5,6,4,5,6],"IntListProperty":[1,2,3,1,2,3]}
There may be some overlap here with Json.Net duplicates private list items. However, I think my problem is even simpler, and I still haven't figured it out.
That is because you are adding items in the constructor. A common approach in deserializers when processing a list is basically:
read the list via the getter
if the list is null: create a new list and assign via the property setter, if one
deserialize each item in turn, and append (Add) to the list
this is because most list members don't have setters, i.e.
public List<Foo> Items {get {...}} // <=== no set
Contrast to arrays, which must have a setter to be useful; hence the approach is usually:
deserialize each item in turn, and append (Add) to a temporary list
convert the list to an array (ToArray), and assign via the setter
Some serializers give you options to control this behavior (others don't); and some serializers give you the ability to bypass the constructor completely (others don't).
I'm pretty sure that this post is not relevant anymore, but for future reference, here a working solution.
Just need to specify that ObjectCreationHandling is set to Replace, i.e. Always create new objects and not to Auto (which is the default) i.e. Reuse existing objects, create new objects when needed.
var data = new SomeData();
var json = JsonConvert.SerializeObject(data);
Console.WriteLine("First : {0}", json);
var data2 = JsonConvert.DeserializeObject<SomeData>(json, new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace });
var json2 = JsonConvert.SerializeObject(data2);
Console.WriteLine("Second: {0}", json2);
I encountered a similar issue with a different root cause. I was serializing and deserializing a class that looked like this:
public class Appointment
{
public List<AppointmentRevision> Revisions { get; set; }
public AppointmentRevision CurrentRevision
{
get { return Revision.LastOrDefault(); }
}
public Appointment()
{
Revisions = new List<AppointmentRevision>();
}
}
public class AppointmentRevision
{
public List<Attendee> Attendees { get; set; }
}
When I serialized this, CurrentRevision was being serialized too. I'm not sure how, but when it was deserializing it was correctly keeping a single instance of the AppointmentRevision but creating duplicates in the Attendees list. The solution was to use the JsonIgnore attribute on the CurrentRevision property.
public class Appointment
{
public List<AppointmentRevision> Revisions { get; set; }
[JsonIgnore]
public AppointmentRevision CurrentRevision
{
get { return Revision.LastOrDefault(); }
}
public Appointment()
{
Revisions = new List<AppointmentRevision>();
}
}
How to apply ObjectCreationHandling.Replace to selected properties when deserializing JSON?
Turns out (I'm in 2019), you can set the list items in your constructor as you were doing in your question. I added the ObjectCreationHandling.Replace attribute above my declaration of the list, then serialising should replace anything stored in the list with the JSON.