I want to project info from a list of base-class instances into a list of derived-class instances, but I keep running into casting exceptions. Here's an example of what I'm trying to do... how do I make it work?
The following code is up at http://ideone.com/CaXQS , if that helps... thanks in advance!
using System;
using System.Collections.Generic;
namespace AddingReversedNumbers
{
public class MyDerivedClass : MyBaseClass, IMyInterface
{
public int InterfaceProperty { get; set; }
public int DerivedClassProperty { get; set; }
public List<int> DerivedClassList { get; set; }
}
public class MyBaseClass
{
public int BaseClassProperty { get; set; }
}
public interface IMyInterface
{
int InterfaceProperty { get; set; }
}
class Program
{
static void Main()
{
//// This code works just fine.
//var derivedList = new List<MyDerivedClass>();
//derivedList.Add(new MyDerivedClass { BaseClassProperty = 10, DerivedClassProperty = 20, InterfaceProperty = 30 });
//derivedList.Add(new MyDerivedClass { BaseClassProperty = 20, DerivedClassProperty = 40, InterfaceProperty = 60 });
//var baseList = derivedList.ConvertAll(x => (MyBaseClass)x);
// This code breaks when ConvertAll() is called.
var baseList = new List<MyBaseClass>();
baseList.Add(new MyBaseClass{ BaseClassProperty = 10 });
baseList.Add(new MyBaseClass{ BaseClassProperty = 20 });
var derivedList = baseList.ConvertAll(x => (MyDerivedClass)x);
}
}
}
In your ConvertAll code you're just casting. You can't cast a base class instance to a derived class instance. For example, what would you expect casting object to FileStream to do? Which file would it refer to?
If you want to create a new object in the ConvertAll projection, just do that:
var derivedList = baseList.ConvertAll
(x => new MyDerivedClass { BaseClassProperty = x.BaseClassProperty });
Related
To cut down on reused code throughout my repository which gets values from another library, I wanted to create extension methods for "parsing"(for lack of a better word) one class to another. How do I implement abstract methods with different parameters.
I can't find anything that answers my question, and I'm not sure it can even be done.
Instead of having something like this in multiple places.
var list = _library.GetList();
var model = list.Select(o => new ClassA()
{
ID = o.ID,
Name = o.Name
}).ToList<ClassA>();
I want extension methods so I can call something like
var list = _library.GetList();
var model = ExtensionClass.ParseMany(list);
But, I want to base this off an abstract class so it can be reused by mutliple different classes, so I have
public abstract class Parser<U, T> where T : class where U : class
{
public abstract T ParseOne(U parser);
public abstract IEnumerable<T> ParseMany(IEnumerable<U> parser);
}
public class ParseA<ClassA, ClassADTO>
{
public override ClassA ParseOne(ClassADTO parser){ // }
}
But it doesn't seem that my parameter that is passed in is the actual object, it says it's a KeyValuePair and now I'm lost.
I expect to able to return a new instance based on my parameter, basically what I already do in my code multiple times.
I guess you can have a generic parser using Func. I just wrote a sample and hope it helps you.
public class ClassA
{
public int SomeNumber { get; set; }
public string SomeString { get; set; }
}
public class ClassB
{
public int OtherNumber { get; set; }
public string OtherString { get; set; }
}
public static class ExecuteParsingFunction
{
public static TDestiny Parse<TOrigin, TDestiny>(TOrigin origin,
Func<TOrigin, TDestiny> parserFunction)
{
return parserFunction(origin);
}
}
public static class ParsingFunctions
{
public static ClassB ParseAToB(ClassA a)
{
return new ClassB { OtherNumber = a.SomeNumber, OtherString = a.SomeString };
}
public static IEnumerable<ClassB> ParseManyAToB(IEnumerable<ClassA> aCollection)
{
foreach(var a in aCollection)
yield return ParseAToB(a);
}
}
public void Sample()
{
var a = new ClassA { SomeNumber = 1, SomeString = "Test" };
var manyAs = new List<ClassA> { a };
var b = ExecuteParsingFunction.Parse(a, ParserFunctions.ParseAToB);
var manyBs = ExecuteParsingFunction.Parse(manyAs, ParserFunctions.ParseManyAToB);
}
The following code illustrates a situation I'm having. The real code use different names and get values in other ways, but they match with what I need an answer. Specifically in lines 76-89 (the only lines I control) I need to extract a variable of type "ICollection" with the values and I don't like none of the used approaches. Is there another approach to do it without to create class "AbstractCollection"?
namespace ConsoleApp1
{
using System.Collections.Generic;
using System.Linq;
interface IEntity
{
string Id { get; }
string Name { get; }
}
class Entity : IEntity
{
public Entity(string id, string name)
{
Id = id;
Name = name;
}
public string Id { get; }
public string Name { get; }
}
interface ICollection<TGeneric>
{
IEnumerable<TGeneric> Items { get; }
}
class Collection<TGeneric> : ICollection<TGeneric> where TGeneric : Entity, IEntity
{
public IEnumerable<TGeneric> Items { get; set; }
}
class AbstractCollection<TConcrete, TAbstract> : ICollection<TAbstract> where TAbstract : class, IEntity
{
public AbstractCollection(ICollection<TConcrete> collection)
{
this._Items = new List<TAbstract>();
if (collection?.Items != null)
{
foreach (TConcrete concreteItem in collection.Items)
{
TAbstract abstractItem = concreteItem as TAbstract;
this._Items.Add(abstractItem);
}
}
}
public IEnumerable<TAbstract> Items
{
get { return this._Items; }
set { this._Items = value?.ToList(); }
}
private IList<TAbstract> _Items { get; set; }
}
class EntityCollection : Collection<Entity>
{
public EntityCollection()
{
var items = new List<Entity>()
{
new Entity("1", "Name1"),
new Entity("2", "Name2"),
new Entity("3", "Name3")
};
Items = items;
}
}
class Context
{
public Context()
{
var concreteItems = new EntityCollection();
// I can modify from this line to the end of the method but not any code before.
// I expected values in "list1" but is null.
var list1 = concreteItems as ICollection<IEntity>;
var list2 = concreteItems as ICollection<Entity>;
var abstractItems = new List<IEntity>();
foreach (Entity concreteItem in concreteItems.Items)
{
IEntity abstractItem = concreteItem as IEntity;
abstractItems.Add(abstractItem);
}
// Why "list3" is null?
var list3 = abstractItems as ICollection<IEntity>;
// I want to avoid class "AbstractCollection"
var list4 = new AbstractCollection<Entity, IEntity>(list2);
// Finally "list5" has value in the way I want it.
var list5 = list4 as ICollection<IEntity>;
}
}
class Program
{
static void Main(string[] args)
{
var context = new Context();
}
}
}
Covariance guides to the solution:
interface ICollection<out TGeneric>
{
IEnumerable<TGeneric> Items { get; }
}
I am trying to learn how to create generic classes with c#. Can someone explain why I get a compile error when I run this program.
I have created the IZooAnimal interface. All zoo animals will implement this interface.
public interface IZooAnimal
{
string Id { get; set; }
}
public class Lion : IZooAnimal
{
string Id { get; set; }
}
public class Zebra : IZooAnimal
{
public string Id { get; set; }
}
The ZooCage will hold animals of the same Type
public class ZooCage<T> where T : IZooAnimal
{
public IList<T> Animals { get; set; }
}
The zoo class have cages
public class Zoo
{
public IList<ZooCage<IZooAnimal>> ZooCages { get; set; }
}
The program that uses the classes
class Program
{
static void Main(string[] args)
{
var lion = new Lion();
var lionCage = new ZooCage<Lion>();
lionCage.Animals = new List<Lion>();
lionCage.Animals.Add(lion);
var zebra = new Zebra();
var zebraCage = new ZooCage<Zebra>();
zebraCage.Animals = new List<Zebra>();
zebraCage.Animals.Add(zebra);
var zoo = new Zoo();
zoo.ZooCages = new List<ZooCage<IZooAnimal>>();
zoo.ZooCages.Add(lionCage);
}
}
When I compile I get the following error:
Error 2 Argument 1: cannot convert from 'ConsoleApplication2.ZooCage<ConsoleApplication2.Lion>' to 'ConsoleApplication2.ZooCage<ConsoleApplication2.IZooAnimal>'
What changes do I have to do in order to make my program run?
#DanielMann's answer is quite good, but suffers from one drawback: the original IList interface cannot be used with the ICage interface. Instead, the ICage has to expose a ReadOnlyCollection, and expose a new method called CageAnimal.
I've also re-written the code using a similar approach. My ICage implementation is much weaker, but it allows you to stick with IList semantics inside.
public interface IZooAnimal
{
string Id { get; set; }
}
public class Lion : IZooAnimal
{
public string Id { get; set; }
}
public class Zebra : IZooAnimal
{
public string Id { get; set; }
}
public interface ICage
{
IEnumerable<IZooAnimal> WeaklyTypedAnimals { get; }
}
public class Cage<T> : ICage where T : IZooAnimal
{
public IList<T> Animals { get; set; }
public IEnumerable<IZooAnimal> WeaklyTypedAnimals
{
get { return (IEnumerable<IZooAnimal>) Animals; }
}
}
public class Zoo
{
public IList<ICage> ZooCages { get; set; }
}
class Program
{
static void Main(string[] args)
{
var lion = new Lion();
var lionCage = new Cage<Lion>();
lionCage.Animals = new List<Lion>();
lionCage.Animals.Add(lion);
var zebra = new Zebra();
var zebraCage = new Cage<Zebra>();
zebraCage.Animals = new List<Zebra>();
zebraCage.Animals.Add(zebra);
var zoo = new Zoo();
zoo.ZooCages = new List<ICage>();
zoo.ZooCages.Add(lionCage);
}
}
Since you want to have multiple cages, but each type of cage can only hold one animal, your model is slightly off.
I rewrote the code as follows:
IZooAnimal is unchanged.
There's a covariant interface ICage that accepts any type of IZooAnimal. That allows you to have a strongly-typed cage for every type of animal.
Then, I have a Cage concrete implementation of ICage. Cage is generic, but you could just as easily make it an abstract class and then make animal-specific cage implementations. For example, if your zebra needs to be fed grass, and your lion needs to be fed meat, you could specialize the implementations of their cages.
Here's the complete code:
public interface IZooAnimal
{
string Id { get; set; }
}
public interface ICage<out T> where T : IZooAnimal
{
IReadOnlyCollection<T> Animals { get; }
}
public class Cage<T> : ICage<T> where T: IZooAnimal
{
private readonly List<T> animals = new List<T>();
public IReadOnlyCollection<T> Animals
{
get
{
return animals.AsReadOnly();
}
}
public void CageAnimal(T animal)
{
animals.Add(animal);
}
}
public class Lion : IZooAnimal
{
public string Id { get; set; }
}
public class Zebra : IZooAnimal
{
public string Id { get; set; }
}
public class Zoo
{
public IList<ICage<IZooAnimal>> Cages { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var lion = new Lion();
var zebra = new Zebra();
var lionCage = new Cage<Lion>();
lionCage.CageAnimal(lion);
var zebraCage = new Cage<Zebra>();
zebraCage.CageAnimal(zebra);
var zoo = new Zoo();
zoo.Cages.Add(lionCage);
zoo.Cages.Add(zebraCage);
}
}
You should define your lists not with the concrete type that implements the interface, but with the interface:
var lionCage = new ZooCage<IZooAnimal>();
lionCage.Animals = new List<IZooAnimal>();
Then your code will work as expected.
The initial code did not work, because it is not allowed to convert concrete types to a generalised type (as #default.kramer pointed out covariance and contravariance).
The solution that i came up is following:
// your ZooCage is still generic
public class ZooCage<T>
{
// but you declare on creation which type you want to contain only!
private Type cageType = null;
public ZooCage(Type iMayContain)
{
cageType = iMayContain;
animals = new List<T>();
}
// check on add if the types are compatible
public void Add(T animal)
{
if (animal.GetType() != cageType)
{
throw new Exception("Sorry - no matching types! I may contain only " + cageType.ToString());
}
animals.Add(animal);
}
// should be generic but not visible to outher world!
private IList<T> animals { get; set; }
}
This code allows you to do:
var lion = new Lion();
var lionCage = new ZooCage<IZooAnimal>(typeof(Lion));
lionCage.Add(lion);
var zebra = new Zebra();
var zebraCage = new ZooCage<IZooAnimal>(typeof(Zebra));
zebraCage.Add(zebra);
But it will throw an error on:
zebraCage.Add(lion);
Now the zoo can be safely extended.
I am trying to figure out how to simplify the following
let's say I have 2 entity classes
public class A
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
}
AND
public class B
{
public int Id { get; set; }
public string Nom { get; set; }
public string Ville { get; set; }
}
classes that are similar, but not the same.
each class has a repository classes it uses for CRUD Operations, for example...
public class RepA
{
public static List<A> GetAll()
{
List<A> list = new List<A>();
A a1 = new A() {Id=1, Name="First A", City="Boston"};
A a2 = new A() {Id=2, Name="First B", City="Chicago"};
A a3 = new A() {Id=3, Name="First C", City="San Francisco"};
list.Add(a1);
list.Add(a2);
list.Add(a3);
return list;
}
public static void SaveAll(List<A> list)
{
foreach (A a in list)
{
Console.WriteLine("Saved Id = {0} Name = {1} City={2}",
a.Id, a.Name, a.City);
}
}
}
AND
public class RepB
{
public static List<B> GetAll()
{
List<B> list = new List<B>();
B b1 = new B() {Id=1, Nom="Second A", Ville="Montreal"};
B b2 = new B() {Id=2, Nom="Second B", Ville="Paris"};
B b3 = new B() {Id=3, Nom="Second C", Ville="New Orleans"};
list.Add(b1);
list.Add(b2);
list.Add(b3);
return list;
}
public static void SaveAll(List<B> list)
{
foreach (B b in list)
{
Console.WriteLine("Saved Id = {0} Name = {1} City={2}", b.Id,
b.Nom, b.Ville);
}
}
}
How would I go about making anonymous call to my repository without having to resort to this, because in my real world example, i have 100 repositories, and not 2.
void Main()
{
ChosenType chosentype = RandomChosenType(); //A or B
switch (chosentype)
{
case ChosenType.A:
var listA = RepA.GetAll();
RepA.SaveAll(listA);
break;
case ChosenType.B:
var listB = RepB.GetAll();
RepB.SaveAll(listB);
break;
default:
break;
}
}
Make a base class or use an interface:
public interface IBase<T>
{
List<T> GetAll();
void SaveAll(List<T> items);
}
public class RepA : IBase<RepA>
{
public List<RepA> GetAll() { return new List<RepA>(); }
public void SaveAll(List<RepA> repA) { }
}
public class RepB : IBase<RepB>
{
public List<RepB> GetAll() { return new List<RepB>(); }
public void SaveAll(List<RepB> repB) { }
}
void Main()
{
IBase chosenType = RandomChosenType();
var list = chosenType.GetAll();
}
You should use a single generic repository. The operations should be handled by injected delegates. A repository could look like this:
public class GenericRepositoryExample
{
public void Save<T>(IList<T> persons, SaveDelegate<T> save)
{
foreach (T person in persons)
{
Console.WriteLine(save(person));
}
}
}
Note that the save delegate is passed to the Save method. The SaveDelegate in your example could be declared as:
public delegate string SaveDelegate<T>(T input);
For ease, I have created a HelperClass containing the delegated functions. In real life helper classes should generally be avoided if possible.
public static class HelperClass
{
public static string FrenchSave(B frenchInput)
{
string result = string.Format("ID = {0}; Name = {1}; City = {2}", frenchInput.Id, frenchInput.Nom, frenchInput.ville);
return result;
}
public static string EnglishSave(A englishInput)
{
string result = string.Format("ID = {0}; Name = {1}; City = {2}", englishInput.Id, englishInput.name, englishInput.city);
return result;
}
}
To illustrate the use of this setup, I have created the following unit test:
[Test]
public void TestGenericRepository()
{
IList<A> aList = new List<A>();
aList.Add(new A() { Id = 1, name = "George", city = "Chicago"});
aList.Add(new A() { Id = 2, name = "Bill", city = "Toledo" });
List<B> bList = new List<B>();
bList.Add(new B() {Id= 1, Nom = "Nathalie", ville = "Paris"});
bList.Add(new B() {Id = 2, Nom = "Michelle", ville = "Lyon"});
GenericRepositoryExample repository = new GenericRepositoryExample();
repository.Save<A>(aList,HelperClass.EnglishSave);
repository.Save<B>(bList,HelperClass.FrenchSave);
}
You can make your repositories implement an interface, say IGetAllSaveAll. Then you can store your repositories in a list, and cast them to that interface. That way you'll be able to call the GetAll function on all of them:
(actually the first interface isn't mandatory, you could directly write it as IEnumerable<object> GetAll()...)
interface IGetAllSaveAll<T>
{
IEnumerable<T> GetAll();
void SaveAll(IEnumerable<T> obj);
}
you'll need to have a base interface:
interface IGetAllSaveAll : IGetAllSaveAll<object>
and to use it:
public class RepA: IGetAllSaveAll
public class RepB: IGetAllSaveAll
....
Then you can keep a dictionnary of all these repositories somewhere:
Dictionnary<Type, IGetAllSaveAll> myDic;
Of course you'll still have to add your repositories to your dictionnary:
myDic.Add(typeof(A), new RepA());
And then to call it:
Type t = RandomChosenType();
myDic[t].GetAll();
The code you posted uses static methods. In order to implement an interface, you will need instance methods. Unless you want to use reflection (should be avoided in my opinion), these methods need to be ignorant of the type. Something like this:
public interface IRepository {
IEnumerable<object> GetAll();
}
And in RepA:
IEnumerable<object> IRepository.GetAll() {
return RepA.GetAll();
}
Instead of storing types, each of your menu selections can just contain an instance of the appropriate repository class in a field of type IRepository. After calling GetAll on one of the instances, you can later cast the result to the specific type (like List<A>) if necessary.
Try this approach based on reflection and some assumptions about your classes' structures:
static void Main(string[] args)
{
var types = Assembly.GetExecutingAssembly().Modules
.SelectMany(m => m.GetTypes())
.Where(t =>
t.GetMethod("GetAll") != null &&
t.GetMethod("SaveAll") != null &&
t.GetMethod("GetAll").ReturnType.IsGenericType)
.Select(t =>
new
{
RepositoryType = t,
ReturnTypeArgument =
t.GetMethod("GetAll").ReturnType.GenericTypeArguments[0]
}
)
.ToList();
(new List<dynamic> { new A(), new B() }).ToList().ForEach(chosenType =>
{
var association = types
.FirstOrDefault(t =>
t.ReturnTypeArgument == chosenType.GetType());
if (association == null)
return;
var repType = association.RepositoryType;
dynamic list = repType.GetMethod("GetAll")
.Invoke(chosenType, new object[] { });
repType.GetMethod("SaveAll")
.Invoke(chosenType, new object[] { list });
});
}
Given your exact scenario, where you've got an enum representing each of the possible data types, here's something that may work.
Map each enum value to a repository type using an attribute. Each repository inherits from a generic class, which implements a basic interface which is not strongly typed. The repo methods change from static to instance members. The base repo class has to do casting to cast object to the appropriate type and back, but the actual repository implementations are strongly typed.
You can take this a step further and try to cache some of the reflection using expression trees so you only have to do it once, but it depends on how optimized you really need to make it.
public enum ChosenType {
[Repo(typeof(RepA))] A = 0,
[Repo(typeof(RepB))] B = 1
}
public class RepoAttribute : Attribute {
public RepoAttribute(Type repoType) { RepoType = repoType; }
public Type RepoType { get; set; }
}
class Program
{
static void Main()
{
ChosenType chosentype = RandomChosenType(); //A or B
// Make an instance of the appropriate repo based on the mapping
// to the enum value.
// This is a moderately expensive call, and there's room for improvement
// by using expression trees and caching lambda expressions.
var repo = (IRepo)Activator.CreateInstance(
((RepoAttribute)typeof(ChosenType).GetMember(chosentype.ToString())
.Single().GetCustomAttributes(typeof(RepoAttribute), false).Single()
).RepoType);
var list = repo.GetAll();
repo.SaveAll(list);
Console.Read();
}
static Random _rand = new Random();
static ChosenType RandomChosenType()
{
return (ChosenType)_rand.Next(0, 2);
}
}
public class A { /* No change */ }
public class B { /* No change */ }
public interface IRepo {
List<object> GetAll();
void SaveAll(List<object> list);
}
public abstract class Repo<T> : IRepo {
List<object> IRepo.GetAll() {
return GetAll().Cast<object>().ToList();
}
void IRepo.SaveAll(List<object> list) {
SaveAll(list.Cast<T>().ToList());
}
public abstract List<T> GetAll();
public abstract void SaveAll(List<T> list);
}
public class RepA : Repo<A> {
public override List<A> GetAll() { /* No change except the signature */ }
public override void SaveAll(List<A> list) { /* No change except the signature */ }
}
public class RepB : Repo<B> {
public override List<B> GetAll() { /* No change except the signature */ }
public override void SaveAll(List<B> list) { /* No change except the signature */ }
}
I am attempting to use ProtoBuf net to serialize an object tree with the classes in the following format:
[ProtoContract]
class MySpecialCollectionList<T> : List<MySpecialCollection<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class MySpecialCollection<T> : List<Special<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
interface IBeast
{
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
using (var fs = File.Create(#"c:\temp\protobuftest.bin"))
{
Serializer.Serialize(fs, collectionList);
fs.Close();
}
}
private MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(cat)},
{new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>() {
specialCollection1, specialCollection2 };
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}
Notice how the class I am serializing (MySpecialCollectionList<T>) is derived from a List<SomeOtherClass<T>>, not just List<T>.
I am struggling to work out where to put "ProtoInclude" attributes to get this to serialize all the items in the MySpecialCollectionList. Any help would be much appreciated.
Inheritance is not an issue here since even if A : B it is not true that Foo<A> : Foo<B>. Note that protobuf-net won't use a non-default constructor, although it is possible to skip the constructor, binding to the field directly (even readonly). While you may have 6 T, I can't see (from the code) that it would ever be in doubt which closed type you intend, and if the closed type is known you should be set.
If you have a Foo<SomeBaseClass> and a number of concrete types inherited from SomeBaseClass then the markers would o on SomeBaseClass.
However, if you have a concrete scenario I can use to reproduce your issue, I'll happily take a look.
Updated re edit:
There are a couple of key points drawn out in the example:
in common with most binding APIs, XmlSerializer and IIRC DataContractSerializer, an item is either a list xor an item with values; if a collection (something implementing IList) has properties itself, they will not be serialized; encapsulation is preferred over inheritance here, i.e. something that has a Name and has a list (rather than has a Name and is a list)
protobuf-net v1 does not support interface-based serialization; v2 does, but as with XmlSerializer and DataContractSerializer you need to explicitly tell it what things it needs to expect; quite nicely, though, we can move the [ProtoMember] onto the interface itself
Here's a fully working version in v2:
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
class MySpecialCollectionList<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<MySpecialCollection<T>> items = new List<MySpecialCollection<T>>();
[ProtoMember(2)]
public List<MySpecialCollection<T>> Items { get { return items; } }
}
[ProtoContract]
class MySpecialCollection<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<Special<T>> items = new List<Special<T>>();
[ProtoMember(2)]
public List<Special<T>> Items { get { return items; } }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
[ProtoContract]
[ProtoInclude(2, typeof(Ant))]
[ProtoInclude(3, typeof(Cat))]
[ProtoInclude(4, typeof(Dog))]
interface IBeast
{
[ProtoMember(1)]
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
public string Name { get; set; }
}
public static class Form1
{
private static void Main()
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
var copy = Serializer.DeepClone(collectionList);
}
private static MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {Items =
{new Special<IBeast>(ant),
new Special<IBeast>(cat),
new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>()
{
Items =
{new Special<IBeast>(ant),
new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>()
{
Items ={
specialCollection1, specialCollection2 }
};
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}