I noticed something strange and there is a possibility I am wrong.
I have an interface IA and class A:
interface IA { .... }
class A : IA { .... }
In other class I have this:
private IList<A> AList;
public IList<IA> {
get { return AList; }
}
But I get compilation error.
But if I change it to:
public IList<IA> {
get { return AList.ToArray(); }
}
Everything is fine.
Why is it?
Why this doesn't work
private IList<A> AList;
public IList<IA> { get { return AList; } }
Exposing the property as IList<IA> would allow you to try to add class B : IA to the list, but the underlying list is really IList<A>, B is not A, so this would blow up in your face. Thus, it is not allowed.
Why this works:
public IList<IA> { get { return AList.ToArray(); } }
Array variance is broken. You can return the list as an array, it will still blow up in your face at runtime if you tried an Add operation (or try to replace an object at a given index with something other than an object of type A, but it's legal at compile time. A different example of this variance at play:
string[] array = new string[10];
object[] objs = array; // legal
objs[0] = new Foo(); // will bite you at runtime
From comments:
So what you suggest to use? How can I make the property return valid
object? How can I make the return value read only?
If consumers only need to iterate over the sequence and not have random, indexed access to it, you can expose the property as an IEnumerable<IA>.
public IEnumerable<IA> TheList
{
get { return AList.Select(a => a); }
}
(The Select is actually not technically needed, but using this will prevent consumers from being able to cast the result to its true underlying List<> type.) If the consumers decide they want a list or an array, they are free to call ToList() or ToArray() on it, and whatever they do with it (in terms of adding, removing, replacing items) will not affect your list. (Changes to the items' properties would be visible.) Similarly, you could also expose the collection an IList<IA> yourself in a safe way
public IList<IA> TheList
{
get { return AList.ToList<IA>(); }
}
Again, this would return a copy of the list, so any changes to it would not affect your underlying list.
Because native arrays are broken. This code is bad, you shouldn't do it, and the C# designers wish desperately they could undo it.
Arrays are covariant but lists are not.
Related
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")
If I try to cast an object of type EntityCollection<MyNamespace.Models.MyEntityClass> to ICollection<Object> I get an InvalidCastException.
Okay, according to the docs, EntityCollection<TEntity> implements ICollection<T>, and MyNamespace.Models.MyEntityClass must descend from Object, right? So why on earth does this fail?
FWIW, I'm trying to do this in a method that generally can add and remove items from what might be an EntityCollection or some other IList or ISet. I need to preserve the change tracking behavior of EntityCollection, because the object is to eventually be able to commit the changes if it's an EC.
Edit
Okay, here's some more specifics of what I'm doing. I'm deserializing JSON, and the target object can have properties that are collections--maybe they're EntityCollections, maybe not. For the sake of simplicity, lets say the members of the collection are always subclasses of EntityObject, whether it's an EntityCollection or not (if I understand the responses so far, I'd have no better luck casting to ICollection<EntityObject> than to ICollection<Object>…right?). This is the part where I run into trouble…
foreach (PropertyInfo prop in hasManys)
{
// This is where I get the InvalidCastException...
ICollection<Object> oldHms = (ICollection<Object>)prop.GetValue(parentObj, null);
JEnumerable<JToken> hmIds = links[FormatPropName(prop.Name)].Children();
if (hmIds.Count() == 0)
{
// No members! Clear it out!
oldHms.Clear();
continue; // breaking early!
}
relType = prop.PropertyType.GetGenericArguments()[0];
// Get back the actual entities we'll need to put into the relationship...
List<EntityObject> newHms = new List<EntityObject>();
foreach (JToken jt in hmIds)
{
// ...populate newHms with existing EntityObjects from the context...
}
// first, delete any missing...
/* Got to use ToList() to make a copy, because otherwise missings is
* still connected to the oldHms collection (It's an ObjectQuery)
* and you can't modify oldHms while enumerating missings.
*/
// This cast will fail too, right? Though it's more easily fixable:
IEnumerable<EntityObject> missings = ((ICollection<EntityObject>)oldHms).Except(newHms).ToList();
foreach (EntityObject missing in missings)
{
oldHms.Remove(missing); // One of my mutable collection operations
}
// add new ones
foreach (EntityObject child in newHms)
{
if (!oldHms.Contains(child)) // Skip if already in there
{
oldHms.Add(child); // another mutable collection operation
}
}
}
}
That's a bit simplified, I have special cases for Arrays (implement ICollection, but aren't generics) and other stuff that I took out. Point is, I need to operate Clear, Add, and Remove on the EntityCollection itself--if that's what it is. Maybe there's another way to do this type of synchronization that I'm missing?
read-write collections cannot be variant.
Take this example:
List<MyClass> list1 = new List<MyClass>();
// assume this would work
ICollection<object> list2 = list1;
list2.Add(new object()); // ooops. We added an object to List<MyClass>!
In principal this kind of casting is only possible for "read-only" interfaces (allowing covariance) or for "write-only" interfaces (allowing contravariance).
One "solution" would involve a wrapper class like this:
public class Wrapper<T> : ICollection<object>
{
private readonly ICollection<T> collection;
public Wrapper(ICollection<T> collection)
{
this.collection = collection;
}
public void Add(object item)
{
// maybe check if T is of the desired type
collection.Add((T)item);
}
public void Clear()
{
collection.Clear();
}
public bool Contains(object item)
{
// maybe check if T is of the desired type
return collection.Contains((T)item);
}
public void CopyTo(object[] array, int arrayIndex)
{
// maybe check if T is of the desired type
collection.CopyTo(array.Cast<T>().ToArray(), arrayIndex);
}
public int Count
{
get { return collection.Count; }
}
public bool IsReadOnly
{
get { return collection.IsReadOnly; }
}
public bool Remove(object item)
{
// maybe check if T is of the desired type
return collection.Remove((T)item);
}
public IEnumerator<object> GetEnumerator()
{
yield return collection;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return collection.GetEnumerator();
}
}
Instead of
EntityCollection<MyNamespace.Models.MyEntityClass> collection = ...;
ICollection<Object> generic = collection ;
you would have to write:
EntityCollection<MyNamespace.Models.MyEntityClass> collection = ...;
ICollection<Object> generic = new Wrapper(collection);
And could adjust the wrapper class at the points marked by comments how to deal with type problems.
Since ICollection<T> hasn't variance, ICollection<MyEntityClass> and ICollection<object> are different types, unrelated to each other.
I'm trying to do this in a method that generally can add and remove
items from what might be an EntityCollection or some other IList or
ISet
So, why don't you work with IList? Looks like you don't care about real type of items in this method.
I was doing some work with interfaces today, when I run into the following scenario. Given these two simple interfaces:
public interface IItem { }
public interface IInventory
{
ICollection<IItem> Items { get; }
}
I made a simple class to implement IInventory, and noticed that this implementation is perfectly fine as written:
public class BasicInventory1 : IInventory
{
private Dictionary<int, IItem> items;
public ICollection<IItem> Items
{
get { return items.Values; }
}
}
But yet, this implementation requires a cast:
public class BasicInventory2 : IInventory
{
private Dictionary<int, IItem> items;
public ICollection<IItem> Items
{
get { return (ICollection<IItem>)items; }
}
}
Why does one require a cast and the other doesn't? Checking the object typing for both collections that are getting returned in either case confirms that they both in fact implement ICollection.
I suspect there is some magic type conversions going on under the hood here, and therefore seems to have something to do with co/contravariance, but I don't quite see what exactly is going on.
Dictionary<int, IItem> does not implement ICollection<IItem>. Simple as that.
It wouldn't make sense to implement that interface because you cannot add to a dictionary without specifying a key. The interface does not make sense.
This is a runtime error because items could refer to a subclass of Dictionary so that the cast might be valid.
I think that if you were to add .Values to the second example, you would not need the cast
public class BasicInventory2 : IInventory
{
private Dictionary<int, IItem> items;
public ICollection<IItem> Items
{
get { return items.Values; }
}
}
This is because items is a Dictionary and that implements ICollection<KeyValuePair<TKey, TValue>>.
This code is NOT VALID and will always generate a runtime error:
public class BasicInventory2 : IInventory
{
private Dictionary<int, IItem> items = new Dictionary<int, IItem>();
public ICollection<IItem> Items
{
get
{
return (ICollection<IItem>) items;
}
}
}
A Dictionary<int, IItem> does NOT implement ICollection<IItem>, whereas the type returned from Dictionary<int, IItem>.Values does.
So the answer is:
The first case is ok because Values is of the correct type.
In the second case, the compiler knows that you are trying to return the wrong type and so it gives you a compile error.
If you override the error with a case, you will get a runtime BadCastException.
In BasicInventory1 you return items.Values in BasicInventory2 you return only items.
.Values returns a ICollection, so no cast is need.
MSDN:
Dictonary
Values
In the second code you use the dictionary as return value where in the first code you use the values. Dictionary<int,IItems> inherits from ICollection<KeyValuePair<int,IItems>> thus is not ICollection<IItems>. Therefore you need the cast.
Is it possible to check if the list contains an object of given (but dynamic) type, derrived from same basic abstract class?
The main problem is not about the list, but about comparing types itself.
In single variables and static variables, it's easy:
if(someVariable is int)
Checking the list with static type is also easy, like:
SomeList.OfType<int>().Any()
or
(from _Object in SomeList.OfType<int> where _Object is int select _Object).Count() == 0
but I cant't handle it if the type I want to check is dynamic, f.e. passed as method parameter:
abstract class BasicClass;
class DerivativeOne : BasicClass { }
class DerivativeTwo : BasicClass { }
// in main:
List<BasicClass> _List = new List<BasicClass>();
DerivativeOne a = new DerivativeOne();
DerivativeTwo b = new DerivativeTwo();
DerivativeOne c = new DerivativeOne();
if(!CheckIfTypeExistsInList(a, _List)
{
_List.Add(a);
}
if(!CheckIfTypeExistsInList(b, _List)
{
_List.Add(b);
}
if(!CheckIfTypeExistsInList(c, _List)
{
_List.Add(c); // this is what I don't want to happen,
// because I already have one object of type DerivativeOne in my list.
}
// the function:
bool CheckIfTypeExistsInList(BasicClass pObject, List<BasicClass> pList)
{
/// few attempts:
pList.OfType<(pObject.GetType()>().Any(); // attempt one, error
return (from _Object in SomeList.OfType<(pObject.GetType())> where _Object is int select _Object).Count() == 0; // attempt two, error
}
PS. I am aware that the code doesn't look neat, but I tried to show just the problem itself, skipping extra logic and stuff.
PS2. I am aware that the solution to the problem would be just to put some attribute to BasicClass and make each derivative to have unique value of the attribute, but still - I'm not looking for another route to solve the problem, I'm just interested if it's possible to do it "this" way.
When the type is known only at runtime, you cannot use it in a generic without using reflection. However, your task is simpler than that - you can use type equality to achieve the results that you want:
Type targetType = pObject.GetType();
if (SomeList.Any(o => targetType.Equals(o.GetType()))) {
...
}
In a C# class I have a list and two different getters for the list:
private List<A> a;
public List<A> EveryA
{
get
{
if (a == null) a = new List<A>();
return a;
}
}
public List<A> FilteredA
{
get
{
return EveryA.FindAll(a => a.IsInFilter);
}
}
Now my question is: how about the syntax FilteredA.Add(this);?
It compiles and runs but it cannot add any item to any list.
Should a better compiler have to notify the (small) problem?
They are not the same list. This is not something the compiler can check for you, since the compiler can't really read your mind. Check the documentation for List<T>.FindAll
The result is a list, but it isn't the same list (how could it be? your original list isn't filtered!).
You should be able to add items to the list returned by FilteredA, except they won't show up in a.
I suggest you use LINQs Where instead, returning an IEnumerable<T>. That way, it is obvious that the result of FilteredA shouldn't be changed, only iterated over:
public IEnumerable<A> FilteredA
{
get { return EveryA.Where(a => a.IsInFilter); }
}
No. Why should it notify you about this? It is completely ok.
FilteredA doesn't return a but a new instance of a List<A>.
FilteredA.Add(this); adds this to this new instance.
See this code:
var filteredA = FilteredA;
int count1 = filteredA.Count;
filteredA.Add(this);
int count2 = filteredA.Count;
Assert.AreEqual(count1 + 1, count2);
This shows, that the new item IS added to the list. But to that new instance that is independent of the list inside your class.
FindAll returns a new list. You're adding the new item to the new list but not retaining a reference to the new list, I suppose. The semantics would be clearer if the filtered list came from a method rather than a property.
public List<A> FilteredA returns some output of the FindAll method, as a List<A>. This will not be the same object as EveryA so when it goes out of scope your addition will be lost.
It's not really a compiler issue - since the code is valid it will compile just fine. The problem is more on a code quality level. To catch something like this, you could use a tool like FxCop to analyze your code.
Both methods can be seen as query methods. You should not expose the result as a List, but rather an IEnumerable or A[]. If you want to add an item to the list, do so with an Add method.
private List<A> items = new List<A>();
public IEnumerable<A> EveryA
{
get { return items; }
}
public IEnumerable<A> FilteredA
{
get { return items.Where(item => item.IsInFilter); }
}
public void AddItem(A item)
{
items.Add(item);
}