简体   繁体   中英

Protobuf-net KeyedCollection serialization

I need to serialize/deserialize a KeyedCollection with protobuf.net, can I just serialize a list?

If so, what is the most efficient way to convert-back the List to the KeyedCollection?

Here follows a sample code that shows the case:

public class FamilySurrogate
{
    public List<Person> PersonList { get; set; }

    public FamilySurrogate(List<Person> personList)
    {
        PersonList = personList;
    }


    public static implicit operator Family(FamilySurrogate surrogate)
    {
        if (surrogate == null) return null;

        var people = new PersonKeyedCollection();
        foreach (var person in surrogate.PersonList)  // Is there a most efficient way?
            people.Add(person);

        return new Family(people);

    }

    public static implicit operator FamilySurrogate(Family source)
    {
        return source == null ? null : new FamilySurrogate(source.People.ToList());
    }

}

public class Person
{
    public Person(string name, string surname)
    {
        Name = name;
        Surname = surname;
    }
    public string Name { get; set; }
    public string Surname { get; set; }
    public string Fullname { get { return $"{Name} {Surname}"; } }
}

public class PersonKeyedCollection : System.Collections.ObjectModel.KeyedCollection<string, Person>
{        
    protected override string GetKeyForItem(Person item) { return item.Fullname; }
}

public class Family
{
    public Family(PersonKeyedCollection people)
    {
        People = people;
    }

    public PersonKeyedCollection People { get; set; }
}

Solution?

.NET Platform Extensions 6 has an implementation of the KeyedCollection, KeyedByTypeCollection Class . This has a constructor which accepts an IEnumerable. The downside to this implementation is that the keys are the items , and it doesn't appear to allow you to change that. If you're already inheriting KeyedCollection, you may as well follow the implementation here and go by Microsoft's lead; they just iterate and call Add() .

See also

Previous thoughts

I'm also trying to tackle this issue from a Linq query perspective, possibly related posts:

The core issue seems to be that KeyedCollectedion does not contain a constructor that takes any form of ICollection to initialize its data with. The base class of KeyedCollection, Collection , does however. The only option seems to be writing your own constructor for your KeyedCollection class that iterates over a collection and adds each element to the current instance.

using System.Collections.Generic;
using System.Collections.ObjectModel;

public class VariableList<T> : KeyedCollection<string, T>
{
    // KeyedCollection does not seem to support explicitly casting from an IEnumerable,
    // so we're creating a constructor who's sole purpose is to build a new KeyedCollection.
    public VariableList(IEnumerable<T> items)
    {
        foreach (T item in items)
            Add(item);
    }

    // insert other code here
}

This seems really inefficient though, so I hope someone corrects me...

Edit: John Franco wrote a blogpost wherein they hack together a solution for genericly casting a List with covariants (in 2009.) This doesn't look like a very good way to do things.

Looking at System.Linq.Enumerable's implementation of ToList, Linq also iterates and Adds to the new collection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM