I have a list of string array and I would like to make both collection read-only.
So I have this code:
public XmlPatternTree(IList<string> nodeNames, IList<IList<string>> attributeNames,
IList<IList<string>> attributeValues) : this()
{
NodeNames = new ReadOnlyCollection<string>(nodeNames);
AttributeNames = new ReadOnlyCollection<ReadOnlyCollection<string>>();
AttributeValues = attributeValues;
Depth = NodeNames.Count;
}
My issue is that AttributeNames and AttributeValues assignments causes a compilation error, it seems that I can create a ReadonlyCollection of ReadonlyCollection from a non-readonly collection of non-readonly objects.
Is there something I can do other than looping over all the values and add them in the list ?
Thanks
If you change your type from IList<string> to just List<string>, then this should work:
attributeNames.Select((x) => x.AsReadOnly()).ToList().AsReadOnly();
If you can't modify your method signature (i.e. you have to keep IList<string>), then you can do this:
attributeNames.Select((x) => x.ToList().AsReadOnly()).ToList().AsReadOnly();
If the version of the .net framework is greater then 4.0 the generic version of List<> implements the IReadOnlyCollection<> interface.
If it is more convenient for you, you can change your signature from IList<ILIst<>> to List<List<>> and should work fine.
AttributeNames = attributeNames;
AttributeValues = attributeValues;
Just a note on the covariance of the IReadOnlyList<out T> type (similar to vasil oreshenski's answer).
If you decide to have:
public XmlPatternTree(IReadOnlyList<string> nodeNames,
IReadOnlyList<IReadOnlyList<string>> attributeNames,
IReadOnlyList<IReadOnlyList<string>> attributeValues) : this()
{
NodeNames = nodeNames;
AttributeNames = attributeNames;
AttributeValues = attributeValues;
}
public IReadOnlyList<string> NodeNames { get; private set; }
public IReadOnlyList<IReadOnlyList<string>> AttributeNames { get; private set; }
public IReadOnlyList<IReadOnlyList<string>> AttributeValues { get; private set; }
public int Depth => NodeNames.Count;
in your class, then the covariance mentioned means you can use reference conversions, and not any wrapping inside another class, as in:
var nn = new List<string>();
var an = new List<string[]>();
var av = new List<string[]>();
// populate 'nn', 'an', and 'av'
// the following compiles with no wrapper class:
var tree = new XmlPatternTree(nn, an, av);
Of course, people can cast the interfaces back to the actual types, like List<string[]>, and modify the collections without using reflection, if they guess that the type is really that list of arrays. However, that would be quite malignant, so you could assume it is no problem if only "good" people use your class
PS! What I said and coded above with IReadOnlyList<out T> could just as well have been done with IReadOnlyCollection<out T> since it is covariant ("out") as well. You would just not have the indexer access on the properties (such as var name = tree.AttrbuteNames[idx1][idx2]). But then you could use HashSet<> and similar which are not IReadOnlyList<>.
Related
I'm having a bit of a problem implementing an interface from a nuget package.
There is a property in the interface that looks like this: IList<IInterfaceInstance> Implements {get;}
My problem is casting from List<InterfaceInstance> to IList<IInterfaceInstance>.
This is what I'm trying to do and it gives me the following exception:
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var ins1 = new InterfaceInstance() {Id = "1"};
var ins2 = new InterfaceInstance() {Id = "2"};
List<InterfaceInstance> imps = new List<InterfaceInstance>() {ins1, ins2};
IList<IInterfaceInstance> implements = imps as IList<IInterfaceInstance>;
foreach( var imp in implements) {
Console.WriteLine(imp.Id);
}
}
private class InterfaceInstance : IInterfaceInstance
{
public string Id { get; set; }
public string Name { get; set; }
}
public interface IInterfaceInstance
{
public string Id { get; set; }
public string Name { get; set; }
}
}
As per the documentation:
The as operator explicitly converts the result of an expression to a
given reference or nullable value type. If the conversion is not
possible, the as operator returns null.
In general, generic types don't allow variance of its arguments, meaning you cannot cast to a different type. This is why implements is null and it fails when trying to perform the foreach.
To achieve your intention, you'll have to cast each independent item to IInterfaceInstance, not the whole list.
You could use linq to select a new ienumerable, passing a lambda to cast each InterfaceInstance as IInterfaceInstance:
IList<IInterfaceInstance> implements = imps.Select(interfaceInstance => (IInterfaceInstance)interfaceInstance).ToList();
IList is not covariant, so you can't cast List<class> to IList<interface>, and your as operator returns null. use IEnumerable<IInterfaceInstance> instead if all you want to do is iterate over the items.
You cannot cast directly to IList like that because it is not co-variant. Otherwise you would be able to add something that implements IInterfaceInstance but isn't InterfaceInstance into a list that should only have InterfaceInstance. Instead you'd have to cast each item like this.
IList<IInterfaceInstance> implements = imps
.Cast<IInterfaceInstance>()
.ToList() as IList<IInterfaceInstance>;
Alternatively you can cast to IEnumerable<IInterfaceInstance> because it is co-variant because it only allows you to pull items out.
IEnumerable<IInterfaceInstance> implements = imps as IEnumerable<IInterfaceInstance>;
How Can I dynamically cast at runtime.That is I am passing a child class object in the parent class object.
public abstract class tObject
{
public tObject[] someMthode(){;}
}
public class myClass : tObject
{
public string Oth0 { get; set; }
public string Oth1 { get; set; }
public string Oth2 { get; set; }
}
I want
myClass mc=new myClass();
tObject to=mc;
myClass[] mcArray=(myClass[])mc.someMthode();//System.InvalidCastException
//Unable to cast object of type 'tObject[]' to type 'myClass[]'
but when check any element of mcArray is correct
if (mcArray[0] is myClass)
{
//return true and run this ;
}
In fact I want cast when a method return array of tObject according to the base class :
subClass[] mcArray=(subClass[])instanceOfsubClass.someMthode()
subClass or myClass and ... are unknown class , and i don't know theirs name.
Solution
public T[] Cast<T>(tObject[] mcArray ) where T : tObject
{
if (mcArray != null)
{
int cnt = mcArray.GetLength(0);
T[] t = new T[cnt];
for (int i = 0; i < cnt; i++)
{
t[i] = (T)mcArray[i];
}
return t;
}
return null;
}
Thanks all for replies.
C# does not support that kind of array conversion. C# does -- unfortunately! -- support dangerous array covariance. That is, if you had an array myClass[] then you could implicitly convert it to an array tObject[]. This means that you can do this:
Tiger[] tigers = new Tiger[10];
Animal[] animals = tigers;
animals[0] = new Turtle();
and now we have a turtle inside an array of tigers. This crashes at runtime.
That's bad enough, but you want it to go the other way -- I have an array of animals and I'd like it to be treated as an array of tigers. That does not work in C#.
As other answers have noted, you'll need to make a second array and copy the contents of the first to the second. There are a number of helper methods to do so.
Maybe?
myClass mc = new myClass();
tObject to = mc;
//myClass[] mcArray = (myClass[])mc.someMthode();//System.InvalidCastException
//Unable to cast object of type 'tObject[]' to type 'myClass[]'
var mcArray = Array.ConvertAll(mc.someMthode(), item => (myClass) item);
Well, you can call IEnumerable.Cast for that:
var myArr = mc.someMethod().Cast<MyClass>().ToArray();
As MyClass[] implements IEnumerable<MyClass>.
EDIT: What you want is quite dangerous. Look the following code:
subClass[] mcArray=(subClass[]) new BaseClass[] {...};
If this conversion would work we could now simply make the following also:
mcArray[0] = new AnotherClass();
Now you have an array of subClasses containin one item of AnotherClass also.
If you do not know the type at compile-time you cannot expect the compiler to provide any compile-time-logic for a type it doesn´t know. Thus casting to an unknown type and calling members on isn´t supported. However you may achieve this using reflection:
var runtimeType = myArr[0].GetType();
var mi = runtimeType.GetMethod("SomeMethod");
var value = mi.Invoke(myArr[0]);
This is similar to the call
var value = ((subClass)myArr[0]).SomeMethod();
Why not solve it one step up the chain and make someMethod (spelling corrected) generic:
public abstract class tObject<T> where T:tObject
{
public T[] someMethod(){;}
}
public class myClass : tObject<myClass>
{
public string Oth0 { get; set; }
public string Oth1 { get; set; }
public string Oth2 { get; set; }
}
now myClass.someMethod returns a myclass[] and that problem is solved. However, since I'm assuming that tObject does other things that just create an array of tObjects, it may cause other problems that aren't inferrable from the code you provided.
Plus it's not 100% foolproof. There's nothing stopping you (or someone else) from defining:
public class myWeirdClass : tObject<myClass>
{
}
now myWeirdClass.someMethod also returns a myClass[], and the pattern is broken. Unfortunately there's no generic constraint that requires that the generic parameter be the defining class itself, so there's no way to prevent this flaw at compile-time.
Another option may be to move the array creation outside of the class itself, since it is a code smell in itself:
public class ObjectFactory<T> where T:tObject
{
public T[] SomeMethod()
{
... create an array of Ts
}
}
public interface IDic
{
int Id { get; set; }
string Name { get; set; }
}
public class Client : IDic
{
}
How can I cast List<Client> to List<IDic>?
You can't cast it (preserving reference identity) - that would be unsafe. For example:
public interface IFruit {}
public class Apple : IFruit {}
public class Banana : IFruit {}
...
List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());
// Eek - it's a banana!
Apple apple = apples[0];
Now you can convert a List<Apple> to an IEnumerable<IFruit> in .NET 4 / C# 4 due to covariance, but if you want a List<IFruit> you'd have to create a new list. For example:
// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();
// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();
But this is not the same as casting the original list - because now there are two separate lists. This is safe, but you need to understand that changes made to one list won't be seen in the other list. (Modifications to the objects that the lists refer to will be seen, of course.)
A Cast iterator and .ToList():
List<IDic> casted = input.Cast<IDic>().ToList() will do the trick.
Originally I said covariance would work - but as Jon has rightly pointed out; no it won't!
And originally I also stupidly left off the ToList() call
I too had this problem and after reading Jon Skeet's answer I modified my code from using List<T> to use IEnumerable<T>. Although this does not answer the OP's original question of How can I cast List<Client> to List<IDic>, it does avoid the need to do so and thus may be helpful to others who encounter this issue. This of course assumes that the code that requires the use of List<IDic> is under your control.
E.g.:
public void ProcessIDic(IEnumerable<IDic> sequence)
{
// Implementation
}
Instead of:
public void ProcessIDic(List<IDic> list)
{
// Implementation
}
If you can use LINQ then you can do this...
List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();
Its only possible by creating new List<IDic> and transfering all elements.
In .Net 3.5, you can do the following:
List<ISomeInterface> interfaceList = new List<ISomeInterface>(list.Cast<ISomeInterface>());
The constructor for List in this case takes an IEnumerable.
list though is only convertible to IEnumerable. Even though myObj may be convertible to ISomeInterface the type IEnumerable is not convertible to IEnumerable.
OfType
You can try something like:
using (var dbContext = YourDatabaseContext())
{
var list = dbContext.Clients.Where(x => x.Happy)
.OfType<IDic>()
.ToList();
}
See https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.oftype
If you want to process the original list without creating a separated reference, you could define the generic method like this:
public void DoIterate<T>(List<T> myCollection) where T : IDic
{
foreach (T item in myCollection)
{
//update a property of interface
item.Name = "new Name";
}
}
Calling this method above to process the list without having to cast specific object to interface:
List<Client> clients = new List<Client>();
DoIterate(clients);
If you don't need to modify the contents of the original list, you can implicitly convert a List into a IReadOnlyList which will let you iterate over it's contents as IDics without creating a new list.
List<Client> myClients = new List<Client>();
myClients.Add(new Client());
IReadOnlyList<IDic> castedClients = myClients;
foreach(IDic val in castedClients)
{
//do something;
}
The conversion can also occur while simply returning the list like so :
public IReadOnlyList<IDic> getClientsAsIDic()
{
return myClients;
}
I'm having a trouble defining a Dictionary for quick accessing Lambda Expressions.
Let's suppose we have a well-known class like this:
class Example
{
public string Thing1;
public DateTime Thing2;
public int Thing3;
}
What a want to do is something like this:
var getters = new Dictionary<string, IDontKnowWhatGoesHere>();
getters.Add("Thing1", x => x.Thing1);
getters.Add("Thing3", x => x.Thing3);
Is this possible?
Edit:
This is my use case for this object:
List<Example> array = new List<Example>();
// We actually get this variable set by the user
string sortField = "Thing2";
array.Sort(getters[sortField]);
Many thanks for your help.
You've got a couple of options. If, as in your example, the things you want to get are all the same type (i.e. String), you can do
var getters = new Dictionary<string, Func<Example, String>>();
However, if they're different types, you'll need to use the lowest common subclass, which in most cases will be Object:
var getters = new Dictionary<string, Func<Example, object>>();
Note that you'll then need to cast the return value into your expected type.
Try:
var getters = new Dictionary<string, Func<Example, object>>();
getters.Add("Thing1", x => x.Thing1);
getters.Add("Thing3", x => x.Thing3);
The first generic type parameter of the Func delegate is the type of the input, and the second generic type parameter is the type of the output (use object because you've different output types).
More about Func: Func<T, TResult> Delegate
var getters = new Dictionary<string, Expression<Func<Example, object>>>();
However, string Thing1 should be public.
I really think you are thinking about this in the wrong way. Why use a dictionary at all? If your class definition is correct, then just use a List<Example>.
List<Example> dataList = new List<Example>();
dataList.Add(new Example { Thing1 = "asdf", Thing2 = "qwert", Thing3 = 2 });
Then you can use linq on it.
IEnumerable<Example> sortedByT3 = dataList.OrderBy(x => x.Thing3);
sortedByT3.Last().Thing2 = "hjkl";
You can also use a dynamic order by provided by Marc Gravell's answer:
var sortedByString = dataList.AsQueryable().OrderBy("Thing2");
No need for lambdas, just direct access to the data.
As everyone has said, you need to make the members public. I would suggest you change it to the following:
public class Example
{
public string Thing1 { get; set; }
public string Thing2 { get; set; }
public int Thing3 { get; set; }
}
I have a class that I fill from the database:
public class Option<T>
{
public T Value { get; set; }
public T DefaultValue { get; set; }
public List<T> AvailableValues { get; set; }
}
I want to have a collection of them:
List<Option<T>> list = new List<Option<T>>();
Option<bool> TestBool = new Option<bool>();
TestBool.Value = true;
TestBool.DefaultValue = false;
list.Add(TestBool);
Option<int> TestInt = new Option<int>();
TestInt.Value = 1;
TestInt.DefaultValue = 0;
list.Add(TestInt);
It doesn't seem to work. Ideas?
I suspect you really want a nongeneric base class - otherwise there's really nothing in common between the different Option<T> closed types.
I understand what you're trying to do, but .NET generics don't allow you to express that relationship. It's like trying to do a map from Type to an instance of that type... it just doesn't fly :(
You have to provide a type instead of your template parameter:
List<Option<T>> list = new List<Option<T>>();
becomes
List<Option<bool>> list = new List<Option<bool>>();
Adding items of type Option<int> to that same list won't work, but that is a separate issue than what I've addressed above.
Firts line you must say wich type T is.
List<Option<bool>> list = new List<Option<bool>>();
And also you can't put that TestInt in this list...
What you're doing only works with heterogeneous lists.
List<T> is an homogeneous list of type T, meaning all elements have to be of type T. Because Option<bool> and Option<int> do not have a common ancestor other than object you can't do that unless you use a List<object> or the old ArrayList, both of which act as heterogeneous lists.
Think of retrieving objects from that list:
list.Add(TestBool);
list.Add(TestInt);
for(int i = 0; i < list.Count; i++)
{
list[i].Value // <- what's the type of this?
}