I am using Protobuf-net to serialize a custom nested list. I understand that native lists cannot be nested directly, which is why I have used a container object for the inner list. However, I would also like to make my container objects IEnumerable but this means Protobuf-net throws it out with the error:
Nested or jagged lists and arrays are not supported
Here is an example of my list structure which causes the error:
[ProtoContract]
public class MyOuterList<T>
{
[ProtoMember(1)]
readonly List<MyInnerList<T>> nestedData = new List<ObjectList<T>>();
}
[ProtoContract]
public class MyInnerList<T> : IEnumerable<T>
{
[ProtoMember(1)]
private readonly List<T> data = new List<T>();
}
The fix is to remove IEnumerable from MyInnerList but obviously that prevents it being directly iterable. Is there a sneaky attribute like [ProtobufCustomObjectSoPleaseIgnoreIEnumerable] that could be used?
The best alternative I have come up with so far is to use an Enumerable property as shown below but I fear that the property could still be cast back to a list again. I would prefer to be using GetEnumerator/yield in some way but I can't see how.
[ProtoContract]
public class MyInnerList<T>
{
[ProtoMember(1)]
private readonly List<T> data = new List<T>();
public IEnumerable<T> Data
{
get { return this.data; }
}
}
Is there a sneaky attribute like [ProtobufCustomObjectSoPleaseIgnoreIEnumerable] that could be used?
yup:
[ProtoContract(IgnoreListHandling=true)]
public class MyInnerList<T> : IEnumerable<T>
{
[ProtoMember(1)]
private readonly List<T> data = new List<T>();
}
sneaky is sneaky. IgnoreListHandling has the intellisense documentation:
If specified, do NOT treat this type as a list, even if it looks like one.
Also, due to multiple requests like this one, I plan on looking at implementing support for jagged arrays / lists shortly. The plan is to basically get the runtime to spoof the wrapper with a member (field 1) in the serializer's imagination, so you can use List<List<T>> and it'll work just like your model above (it will even be wire-compatible, since you sensibly chose field 1).
Related
Some custom types that implement IEnumerable don't necessarily have backing collections. They could be generated dynamically, for example using 'yield' or LINQ. Here is an example:
public class SOJsonExample
{
public class MyCustomEnumerable : IEnumerable<KeyValuePair<int,float>>
{
public List<int> Keys { get; set; } = new List<int> { 1, 2, 3 };
public List<float> Values { get; set; } = new List<float> { 0.1f, 0.2f, 0.3f };
public IEnumerator<KeyValuePair<int, float>> GetEnumerator()
{
var kvps =
from key in Keys
from value in Values
select new KeyValuePair<int, float>(key, value);
return kvps.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
}
I have discovered that the default serialization by Json.NET is to enumerate each value and store the values in a JavaScript array (which I don't want). The default deserializer will then fail to deserialize the collection as it can't be populated. In these cases, I'd instead want Json.NET to skip the default JavaScript array serialization, and just store the members of the class.
All I want is my keys and values - is there any shortcut way to do this, or do I have to implement my own custom serializer?
Checked this and this, neither of which are exactly my question. Scanned the documentation as well, but didn't find what I was looking for (perhaps I looked in the wrong place).
(Edit #1 - improved clarity)
(Edit #2 - answered my own question...see below)
(Edit #3 - updated to more modern C#)
Answered my own question - see the excellent documentation on IEnumerable, Lists, and Arrays:
.NET lists (types that inherit from IEnumerable) and .NET arrays are converted to JSON arrays. Because JSON arrays only support a range of values and not properties, any additional properties and fields declared on .NET collections are not serialized. In situations where a JSON array is not wanted the JsonObjectAttribute can be placed on a .NET type that implements IEnumerable to force the type to be serialized as a JSON object instead.
So, for my example:
[JsonObject]
public class MyCustomEnumerable : IEnumerable<KeyValuePair<int,float>>
{
...
}
This is an issue I have seen in two different jobs that use a 3-tier structure and haven't found a clean way around it. This also applies to using LINQ statements I believe.
I have 2 classes, one is an object and the other is a defined collection of those objects that might have some additional functionality in it:
public class TestObject
{
public Int32 id {get; set;}
public string value {get; set;}
}
public class TestObjectCollection: List<TestObject>
{
public TestObject Get(Int32 id)
{
return this.FirstOrDefault(item => item.id==id);
}
}
Say I use a lambda expression like:
List<TestObject> result = data.Where(item => item.id > 0).ToList();
Is there an easy way to convert that list of objects to my defined collection without doing something like this:
TestObjectCollection resultAsCollection = new TestObjectCollection()
resultAsCollection.AddRange(result);
It seems like there should be a way to cast my GenericList returned by the Lambda expression to my TestObjectCollection without the added step looping through my returned results.
No, there isn't. ToList creates a List<T> - and there's no way of casting a plain List<T> to a TestObjectCollection without creating a new TestObjectCollection.
Personally I'd avoid creating a collection deriving from List<T> at all (I'd almost always use composition instead) but if you really want to have that collection, the simplest approach is to create your own extension method:
public static class TestEnumerable
{
public static TestObjectCollection ToTestObjectCollection(this IEnumerable<TestObject> source)
{
return new TestObjectCollection(source);
}
}
... and implement the appropriate constructor, of course, which can probably just chain to the List(IEnumerable<T>) constructor. Then you can write:
var resultAsCollection = data.Where(item => item.id > 0).ToTestObjectCollection();
It seems like there should be a way to cast my GenericList returned by
the Lamda expression to my TestObjectCollection without the added step
looping through my returned results
There isn't because with generic collections you can use covariance and not contravariance.
Other than Jon's solution (which is good) you can create constructor overload:
public class TestObjectCollection: List<TestObject>
{
public TestObjectCollection(IEnumerable<TestObject> list) { AddRange(list);}
...
}
Usage:
var resultAsCollection = new TestObjectCollection(data.Where(item => item.id > 0));
This example is in C# but the question really applies to any OO language. I'd like to create a generic, immutable class which implements IReadOnlyList. Additionally, this class should have an underlying generic IList which is unable to be modified. Initially, the class was written as follows:
public class Datum<T> : IReadOnlyList<T>
{
private IList<T> objects;
public int Count
{
get;
private set;
}
public T this[int i]
{
get
{
return objects[i];
}
private set
{
this.objects[i] = value;
}
}
public Datum(IList<T> obj)
{
this.objects = obj;
this.Count = obj.Count;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return this.objects.GetEnumerator();
}
}
However, this isn't immutable. As you can likely tell, changing the initial IList 'obj' changes Datum's 'objects'.
static void Main(string[] args)
{
List<object> list = new List<object>();
list.Add("one");
Datum<object> datum = new Datum<object>(list);
list[0] = "two";
Console.WriteLine(datum[0]);
}
This writes "two" to the console. As the point of Datum is immutability, that's not okay. In order to resolve this, I've rewritten the constructor of Datum:
public Datum(IList<T> obj)
{
this.objects = new List<T>();
foreach(T t in obj)
{
this.objects.Add(t);
}
this.Count = obj.Count;
}
Given the same test as before, "one" appears on the console. Great. But, what if Datum contains a collection of non-immutable collection and one of the non-immutable collections is modified?
static void Main(string[] args)
{
List<object> list = new List<object>();
List<List<object>> containingList = new List<List<object>>();
list.Add("one");
containingList.Add(list);
Datum<List<object>> d = new Datum<List<object>>(containingList);
list[0] = "two";
Console.WriteLine(d[0][0]);
}
And, as expected, "two" is printed out on the console. So, my question is, how do I make this class truly immutable?
You can't. Or rather, you don't want to, because the ways of doing it are so bad. Here are a few:
1. struct-only
Add where T : struct to your Datum<T> class. structs are usually immutable, but if it contains mutable class instances, it can still be modified (thanks Servy). The major downside is that all classes are out, even immutable ones like string and any immutable class you make.
var e = new ExtraEvilStruct();
e.Mutable = new Mutable { MyVal = 1 };
Datum<ExtraEvilStruct> datum = new Datum<ExtraEvilStruct>(new[] { e });
e.Mutable.MyVal = 2;
Console.WriteLine(datum[0].Mutable.MyVal); // 2
2. Create an interface
Create a marker interface and implement it on any immutable types you create. The major downside is that all built-in types are out. And you don't really know if classes implementing this are truly immutable.
public interface IImmutable
{
// this space intentionally left blank, except for this comment
}
public class Datum<T> : IReadOnlyList<T> where T : IImmutable
3. Serialize!
If you serialize and deserialize the objects that you are passed (e.g. with Json.NET), you can create completely-separate copies of them. Upside: works with many built-in and custom types you might want to put here. Downside: requires extra time and memory to create the read-only list, and requires that your objects are serializable without losing anything important. Expect any links to objects outside of your list to be destroyed.
public Datum(IList<T> obj)
{
this.objects =
JsonConvert.DeserializeObject<IList<T>>(JsonConvert.SerializeObject(obj));
this.Count = obj.Count;
}
I would suggest that you simply document Datum<T> to say that the class should only be used to store immutable types. This sort of unenforced implicit requirement exists in other types (e.g. Dictionary expects that TKey implements GetHashCode and Equals in the expected way, including immutability), because it's too difficult for it to not be that way.
Kind of hacky, and definitely more confusing than it's worth in my opinion, but if your T is guaranteed to be serializable, you can store string representations of the objects in your collection rather than storing the objects themselves. Then even if someone pulls an item from your collection and modifies it, your collection would still be intact.
It would be slow and you'd get a different object every time you pulled it from the list. So I'm not recommending this.
Something like:
public class Datum<T> : IReadOnlyList<T>
{
private IList<string> objects;
public T this[int i] {
get { return JsonConvert.DeserializeObject<T>(objects[i]); }
private set { this.objects[i] = JsonConvert.SerializeObject(value); }
}
public Datum(IList<T> obj) {
this.objects = new List<string>();
foreach (T t in obj) {
this.objects.Add(JsonConvert.SerializeObject(t));
}
this.Count = obj.Count;
}
public IEnumerator<T> GetEnumerator() {
return this.objects.Select(JsonConvert.DeserializeObject<T>).GetEnumerator();
}
}
It's impossible. There's no possible way to constrain the generic type to be immutable. The best that you can possibly do is write a collection that cannot allow the structure of that collection to be modified. There is no way to prevent the collection from being used as a collection of some mutable type.
think that such collections are not match OOP, because this design leads to specific co-relation between independent classes - collection and it's items. How one class can change behavior of other without knowlege of each other?
So suggestions of serialization and so can allow you to do it on hacky way, but better is to decide if it's so required to make collection of immutable items, who trys to change them except your own code? May be better "to not mutate" items rather than try "make them immutable".
I faced the same problem, where I implement an object (say CachedData<T>) which handles a cached copy of the property of another object (say T SourceData). When calling the constructor of CachedData, you pass a delegate which returns a SourceData. When calling CachedData<T>.value, you get a copy of SourceData, which is updated every now and then.
It would make no sense to try caching an object, as .Value would only cache the reference to the data, not the data itself. It would only make sense to cache data types, strings, and perhaps structures.
So I ended up:
Thoroughly documenting CachedData<T>, and
Throwing an error in the constructor if T is neither a ValueType, a Structure, or a String. Some like (forgive my VB): If GetType(T) <> GetType(String) AndAlso GetType(T).IsClass Then Throw New ArgumentException("Explain")
I'd like to have a class "A" with a (for example) SortedList collection "SrtdLst" property, and inside this class "A" allow the addition or subtraction of "SrtdLst" items. But in a instance of the class "A", only allow to get or set the content of the items, not to add new items or subtract the existing ones. In code:
class A
{
public SortedList<string, string> SrtdLst = new SortedList<string, string>();
public A()
{
// This must work:
SrtdLst.Add("KeyA", "ValueA");
// This too:
SrtdLst["KeyA"] = "ValueAAA";
}
}
class B
{
public A a = new A();
public B()
{
// I want the following code to fail:
a.SrtdLst.Add("KeyB", "ValueB");
// But this must work:
a.SrtdLst["KeyA"] = "ValueBBB";
}
}
UPDATE: I want to create a class like System.Data.SqlClient.SqlCommand. For the Stored Procedures you can use the member "DeriveParameters" that fills a collection of "Parameters", so only the value of each item can be modified.
How can this be done?
If you want to ban the modifying operations at compile time, you need a type-safe solution.
Declare an interface for the publicly allowed operations. Use that interface as the property type.
public interface IReadOnlyList<T>
{
T this[int index] { get; }
int Count { get; }
}
Then declare a class that implements that interface and inherits from the standard collection class.
public class SafeList<T> : List<T>, IReadOnlyList<T> { }
Assuming you get the interface definition right, you won't need to implement anything by hand, as the base class already provides the implementations.
Use that derived class as the type of the field that stores the property value.
public class A
{
private SafeList<string> _list = new SafeList<string>();
public IReadOnlyList<string>
{
get { return _list; }
}
}
Within class A, you can use _list directly, and so modify the contents. Clients of class A will only be able to use the subset of operations available via IReadOnlyList<T>.
For your example, you're using SortedList instead of List, so the interface probably needs to be
public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
V this[K index] { get; }
}
I've made it inherit IEnumerable as well, which is readonly anyway, so is perfectly safe. The safe class would then be:
public class SafeSortedList<K, V> : SortedList<K, V>, IReadOnlyDictionary<K, V> { }
But otherwise it's the same idea.
Update: just noticed that (for some reason I can't fathom) you don't want to ban modifying operations - you just want to ban SOME modifying operations. Very strange, but it's still the same solution. Whatever operations you want to allow, "open them up" in the interface:
public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
V this[K index] { get; set; }
}
Of course, that's the wrong name for the interface now... why on earth would you want to ban adding via Add but not ban it via the indexer? (The indexer can be used to add items, just as the Add method can.)
Update
From your comment I think you mean that you want to allow assignment to the value of an existing key/value pair, but disallow assignment to a previously unknown key. Obviously as keys are specified at runtime by strings, there's no way to catch that at compile time. So you may as well go for runtime checking:
public class FixedSizeDictionaryWrapper<TKey, TValue> : IDictionary<TKey, TValue>
{
IDictionary<TKey, TValue> _realDictionary;
public FixedSizeDictionaryWrapper(IDictionary<TKey, TValue> realDictionary)
{
_realDictionary = realDictionary;
}
public TValue this[TKey key]
{
get { return _realDictionary[key]; }
set
{
if (!_realDictionary.Contains(key))
throw new InvalidOperationException();
_realDictionary[key] = value;
}
}
// Implement Add so it always throws InvalidOperationException
// implement all other dictionary methods to forward onto _realDictionary
}
Any time you have an ordinary dictionary and you want to hand it to some method that you don't trust to update the existing values, wrap it in one of these.
EDIT: Original answer is below. As earwicker points out, I hadn't noticed that you aren't asking for it to be readonly - just to prevent the Add operation. That doesn't sound like a good idea to me, as the only difference between Add and the indexer-setter is that Add throws an exception if the element is already present. That could easily be faked up by the caller anyway.
Why do you want to restrict just that one operation?
Original answer
For one thing, don't use public fields. That's a surefire way to run into problems.
It looks like you want a read-only wrapper class round an arbitrary IDictionary. You can then have a public property which returns the wrapper, while you access the private variable from within your class. For example:
class A
{
private SortedList<string, string> sortedList = new SortedList<string, string>();
public IDictionary<string, string> SortedList
{
get { return new ReadOnlyDictionaryWrapper(sortedList);
}
public A()
{
sortedList.Add("KeyA", "ValueA");
sortedList["KeyA"] = "ValueAAA";
}
}
Now you've just got to find a ReadOnlyDictionary implementation... I can't implement it right now, but I'll be back later if necessary...
Just make the list private, and expose it as an indexer:
class A {
private SortedList<string, string> _list;
public A() {
_list = new SortedList<string, string>()
}
public string this[string key] {
get {
return _list[key];
}
set {
_list[key] = value;
}
}
}
Now you can only access the items using the index:
a["KeyA"] = "ValueBBB";
However, as the indexer of the list allows creation of new items, you would have to add code in the indexer to prevent that if you don't want that do be possible.
If the keys are known outside of the class then you can add a ChangeItem(key, newValue) and ReadItem(key) to your wrapper class. Then keep the SortedList private to the class.
Sorry to ask this yet again. I've been reading the related topics here for hours and still don't quite get some of the issues with casting and generics. I'm trying to create our own HashSet class, because we can't use .net 3.5 yet.
The error is in the RetainAll method, below. It compiles fine but I get a runtime error
'Unable to cast object of type 'System.Collections.DictionaryEntry' to type 'mycompany.UnivID' on the foreach statement.
Does this mean I can't cast a generic object to its implementing type? I've tried a couple things like
foreach ((T) obj in set)
but they don't compile.
Relevant code is:
public sealed class HashSet<T> : ISet<T> {
private readonly Hashtable set;
public HashSet() {
this.set = new Hashtable();
}
public void RetainAll(ISet<T> c) {
lock (set) {
foreach (T obj in set) {
if (!c.Contains(obj))
this.set.Remove(obj);
}
}
}
}
The implementing class snippet is
public sealed class UnivIDSet : ISet {
private readonly ISet<UnivID> univIDs;
public UnivIDSet() : base() {
univIDs = new HashSet<UnivID>();
}
}
and then we've got our UnivID class which are actually the objects in the sets.
I think
foreach (T obj in set) {
should be
foreach (T obj in set.Values) {
When you enumerate a HashTable the type the enumerator returns is a DictionaryEntry. Read the Key and Value properties of this object to get what you need.
Hashtables map keys to values. When you iterate over each item in the hashtable, that item is a key/value pair (that's what the DictionaryEntry class is). You probably want to iterate over the Keys property or the Values property instead, depending on how you entered things into the table.
Also note that the Dictionary class is the generic version of a hashtable, so it's probably worth building your HashSet on top of that instead.