I have the following code
public class TestAdaptor
{
private interface ITargetClass
{
Guid Id { get; }
string Name { get; }
}
private class MyTargetClass : ITargetClass
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public MyTargetClass(MySourceClass source)
{
}
}
private class MySourceClass
{
public Guid Id { get; set; }
public string Name { get; set; }
}
private Dictionary<Guid, IEnumerable<ITargetClass>> ConvertItems(Dictionary<Guid, IEnumerable<MySourceClass>> source)
{
return source.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Select(v => new MyTargetClass(v)));
}
}
However this will not compile as the ToDictionary line causes the following error
Cannot implicitly convert type
'System.Collections.Generic.Dictionary<System.Guid,System.Collections.Generic.IEnumerable<TestAdaptor.TestAdaptor.MyTargetClass>>'
to
'System.Collections.Generic.Dictionary<System.Guid,System.Collections.Generic.IEnumerable<TestAdaptor.TestAdaptor.ITargetClass>>' ...\TestAdaptor.cs 38 20
Now it is clearly obvious that MyTargetClass implements ITargetClass but the compiler doesn't pick this up.
For now I am explicitly converting (ITargetClass)new MyTargetClass(v)
But why is this happening in the first place and is there a better way to resolve this?
The compiler wont automatically convert IEnumberable<X> to IEnumerable<Y> even if X : Y because IDictionary is not covariant. The rationale for this is discussed here: IDictionary<,> contravariance? and IDictionary<TKey, TValue> in .NET 4 not covariant
As for getting around it, like you mentioned, you'll have to cast:
With Cast extension method:
kvp => kvp.Value.Select(v => new MyTargetClass(v)).Cast<ITargetClass>()
Explicit cast:
kvp => kvp.Value.Select(v => (ITargetClass) new MyTargetClass(v))
Update:
Just to expand on this because of the confusion between IEnumerable and IDictionary. IEnumerable is covariant. IDictionary is not.
This is just fine:
IEnumerable<ITargetClass> list = new List<MyTargetClass>();
This is not:
IDictionary<object, IEnumerable<ITargetClass>> dict =
new Dictionary<object, List<MyTargetClass>>();
IDictionary inherits from IEnumerable<KeyValuePair<TKey, TValue>>. At issue is the KeyValuePair which is not covariant, which makes IDictionary not covariant.
Select only reports what is being created and not a facet of the objects type. Let the select know what you are using via the as keyword.
Select(v => new MyTargetClass(v) as ITargetClass));
It is not the compiler's job to understand an intention of a developer, for a class may express many interfaces. One has to provide hints to the select statement which ultimately brings it in line with the return object required.
Otherwise you can filter the elements and return an IEnumerable of the interface using OfType IEnumerable extension to return what is required by your method.
.Select(v => new MyTargetClass(v))
.OfType<ITargetClass>()
Related
i have this constructor which was supposed to accept any IEnumerable of any type, and try to convert the values to the desired type
public ChartData(IEnumerable<dynamic> labels, IEnumerable<dynamic> values)
{
this.values = values.Select(x=>(float)Convert.ToString(x));
this.labels = labels.Select(x=>(string)Convert.ToString(x));
}
when i call this constructor i can pass ienumerables of the type string, but not int or float
how can i make a constructor that will accept ienumerables of any type without making tons of overloads?
If you accept any type, that would also include IEnumerable<User> and IEnumerable<Car>, how would you expect to convert them to float and string? It would be much better to have an object that encapsulates a float and a string value, use an interface for that. For example, have an interface like this:
public interface IChartItem
{
float Value { get; set; }
string Label { get; set; }
}
An example class:
public class Foo : IChartItem
{
public int MyProperty { get; set; }
public string Name { get; set; }
public float Value => (float)MyProperty;
public string Label => Name;
}
And now your function could be generic with a constraint:
public void ChartData<T>(IEnumerable<T> items)
where T : IChartItem
{
this.values = items.Select(x => x.Value);
this.labels = items.Select(x => x.Label);
}
Alternative Option
You could also pass in your object along with a couple of Func parameters to get the value and label. For example:
public void ChartData<T>(IEnumerable<T> items,
Func<T, float> valueSelector,
Func<T, string> labelSelector)
{
this.values = items.Select(x => valueSelector(x));
this.labels = items.Select(x => labelSelector(x));
}
And use it like this:
ChartData(foos, f => (float)f.MyProperty, f => f.Name);
If you don't care about the type use non-generic version IEnumerable(which is base interface for IEnumerble<T>):
public ChartData(IEnumerable labels, IEnumerable values)
{
// x is of type Object for both lines.
this.values = values.Select(x=>(float)Convert.ToSingle(x));
this.labels = labels.Select(x=>(string)Convert.ToString(x));
}
Note that
you lose all type safety - someone can easily pass list of files or some other random types as either of arguments
in general generics version is preferable if you deal with value types due to boxing cost. Fortunately in your case you need boxed value anyway or Convert.
Personally I'd only accept IEnumerable<float> (or maybe double) and IEnumerable<String> as arguments - caller of this method will have more knowledge to pick desired fields/conversion than my code trying to guess the type they passed in. If some "any numeric type" would be a desired input I'd check Is there a constraint that restricts my generic method to numeric types? and implement all variant instead of falling back to runtime reflection with dynamic or lack of type safety with non-generic version.
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<>.
I am using C# and I thought I finally had the chance to understand a Generic type. I have several strongly typed objects that need the same static method. Rather than create one static method for each type I thought I could make it generic. Something I have never done and really wanted too.
Here is where I invoke it.
bool isDuplicate = Utilities.GetDuplicates<RoomBookingModel>(roomBookings);
Here is my static method which resides in a static class called Utilities.
public static bool GetDuplicates<T>(List<T> pBookings)
{
foreach (var item in pBookings)
{
var myVal = item.bookingId
}
return true;
}
So I want to get at the values within var item inside the foreach loop so I can do comparisons. It's definately passed pBookings because I can hover and they have a .Count() with a collection of my strongly typed object. I am missing something here, possibly a casting process. I was wondering if anyone could advise me where I am coming up short.
var myVal = item.bookingId - I cannot get the bookingID from item because I am lacking in some basic understanding here. bookingId doesn't exist, I just get access to extension methods such as .toString and .equals
ANSWER OF SORTS What I did based on all of your really helpful assistance. I utilised Anderson Pimentel. I'm probably still off the mark but wanted to garner anyones thoughts here.
So basically I have several booking models, all need checking for duplicates. I really wanted to understand Generics in this way. So what I did is. Created a base class.
public class BookingBaseModel
{
public int BookingID { get; set; }
public DateTime BookingStartDateTime { get; set; }
public DateTime BookingEndDateTime { get; set; }
}
Then had my booking classes all inherit whats common to all. Like this...
public class RoomBookingModel : BookingBaseModel
{
public string RoomName{ get; set; }
}
public class vehicleBookingModel : BookingBaseModel
{
public string vehicleName{ get; set; }
}
Then in my utilities static helper I did this..
public static void GetDuplicates<T>(List<T> items) where T : BookingBaseModel
{
foreach (var item in items)
{
int myId = item.ID;
DateTime startDateTime = item.BookingStartDateTime;
DateTime endDateTime = item.BookingEndDateTime;
//Do you logic here
}
}
Then finally did something like this in corresponding controller action.
RoomController...
Utilities.GetDuplicates<RoomBookingModel>(roomBookings);
VehicleController....
Utilities.GetDuplicates<VehicleBookingModel>(vehicleBookings);
Is this basically how we go about using generics in this way?
The compiler has no hint of what type is T. If you have a base class (or an Interface) which has the bookingId attribute, like BaseModel, you can constrain the generic type like the following:
public class BaseModel
{
public int Id { get; set; }
}
public static bool GetDuplicates<T>(List<T> items) where T : BaseModel
{
foreach (var item in items)
{
var myId = item.Id;
//Do you logic here
}
return true;
}
Once you're inside your GetDuplicates method, you have lost all knowledge of the RoomBookingModel type. That's the point of generic methods - they should be able to act on whatever type has been passed in to them, e.g. the logic within them should be generic across any type.
So your foreach loop is fine - you know you've been given a list of something, and you know lists can be iterated. But inside that foreach, item is just a T. You don't know what actual type it is because any type could have been passed in. So it doesn't make sense to access a specific property or method off of item - for example, what if I called GetDuplicates passing in a List<int>? It wouldn't have a bookingId property.
As written by others, you don't know anything of T. A classical solution, used by LINQ (see for example GroupBy) is to have your method receive a delegate that does the key-extraction, like:
public static bool GetDuplicates<T, TKey>(List<T> pBookings, Func<T, TKey> selector)
{
foreach (var item in pBookings)
{
TKey key = selector(item);
}
return true;
}
You then use it like:
GetDuplicates(pBookings, p => p.bookingId);
If you like to use a generic method, you have to provide also a generic method, which is able to generate a key out of the specified type T. Luckily we have LINQ which already provides the needed parts to build your generic method:
internal class Extensions
{
public static IEnumerable<T> GetDuplicates<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
{
return source.GroupBy(keySelector)
.Where(group => group.Skip(1).Any())
.SelectMany(group => group);
}
public static bool ContainsDuplicates<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
{
return GetDuplicates(source, keySelector).Any();
}
}
By having this (and type inference) you can use these methods e.g. by calling:
var hasDuplicates = roomBookings.ContainsDuplicates(item => item.bookingId);
if(hasDuplicates)
{
Console.WriteLine("Duplicates found:");
foreach (var duplicate in roomBookings.GetDuplicates(item => item.bookingId))
{
Console.WriteLine(duplicate);
}
}
I wonder if generics is really the tool for the job here. Your needs would be better served if each of your strongly typed objects shared a common interface.
"I have several strongly typed objects that need the same static method."
In this situation, all of the classes must share a common feature, such as, for instance, a property BookingId.
So, you'd need to formalize this by extracting this common interface:
public interface IBooking
{
int BookingId{ get; }
}
Make sure that every one of your strongly typed items implements the interface:
public class RoomBooking : IBooking
{
//etc...
}
And now make your static method accept IBooking instances:
public static bool GetDuplicates(IEnumerable<IBooking> pBookings)
{
//does pBookings contain items with duplicate BookingId values?
return pBookings.GroupBy(b => b.BookingId).Any(g => g.Count() > 1);
}
An easy read that isn't obfuscated by the unnecessary use of generics.
Since there are no constraints or hints about what T is, the compiler does not have enough information. Consider
bool isDuplicate = Utilities.GetDuplicates<int>(roomBookings);
Clearly an int does not have a bookingId member.
Every possible specific type for T would have to have a common base class or interface that has a bookingId, and even then you would have to add a generic constraint to your method signature to access that.
Perhaps you are looking for something like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Duplicates
{
public static class EnumerableExtensions
{
public static bool HasDuplicates<T, I>(this IEnumerable<T> enumerable, Func<T, I> identityGetter, IEqualityComparer<I> comparer )
{
var hashSet = new HashSet<I>(comparer);
foreach (var item in enumerable)
{
var identity = identityGetter(item);
if (hashSet.Contains(identity)) return true;
hashSet.Add(identity);
}
return false;
}
public static bool HasDuplicates<T, I>(this IEnumerable<T> enumerable, Func<T, I> identityGetter)
{
return enumerable.HasDuplicates(identityGetter, EqualityComparer<I>.Default);
}
}
public class Booking
{
public int BookingId { get; set; }
public string BookingName { get; set; }
}
public class Customer
{
public string CustomerId { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
var bookings = new List<Booking>()
{
new Booking { BookingId = 1, BookingName = "Booking 1" },
new Booking { BookingId = 1, BookingName = "Booking 1" }
};
Console.WriteLine("Q: There are duplicate bookings?. A: {0}", bookings.HasDuplicates(x => x.BookingId));
var customers = new List<Customer>()
{
new Customer { CustomerId = "ALFKI", Name = "Alfred Kiss" },
new Customer { CustomerId = "ANATR", Name = "Ana Trorroja" }
};
Console.WriteLine("Q: There are duplicate customers?. A: {0} ", customers.HasDuplicates(x => x.CustomerId));
}
}
}
I am trying to to create a generic cache class that will hold a list of objects, and will expose a method that enables to check if an instance of an object is already cached based on Id property:
public class CacheService<T> where T : BaseModel
{
private List<T> _data = new List<T>();
public void Check(T obj)
{
if (_data.Contains(r => r.Id.Equals(obj.Id))
{
//Do something
}
}
}
public class BaseModel
{
public int Id { get; set; }
}
I am getting a compiler error on the Contains() command, saying:
Cannot convert lambda expression to type 'T' because it is not a delegate type
How can I achieve my goal?
You can use Linq:
bool contains = _data.Any(r => r.Id.Equals(obj.Id));
or List.Exists:
bool contains = _data.Exists(r => r.Id.Equals(obj.Id));
Use the LINQ function Any instead of Contains. For List<T>, the Contains method is defined to take a T.
If it exists, what is the C# equivalent of the following Java code:
new HashMap<Class<? extends BaseClass>, Integer>();
I currently use new Dictionary<Type, int>(), which is more like new HashMap<Class<?>, Integer>() which is obviously not the same.
(Ignore the differences between HashMap and Dictionary)
Edit: To clarify, I am not trying to define a new class, simply create an instance of HashMap/Dictionary.
There is no equivalent of the Java wildcard in C#. In Java, the type for types is Class<T> where T is the class itself. The equivalent in C# is the type Type, which is not generic. So it seems that the best you can do is to have, as you said, a Dictionary<Type, int>, and if it's encapsulated in a class you can restrict what you put in the dictionary in the code (so it will just be a runtime check):
private Dictionary<Type, int> myDictionary = new Dictionary<Type, int>();
public void Add(Type type, int number) {
if (!typeof(BaseClass).IsAssignableFrom(type)) throw new Exception();
myDictionary.Add(type, number);
}
You can even implement your own IDictionary with that logic.
UPDATE
Another runtime trick I can think of is to use a wrapper class for your types:
public class TypeWrapper<T>
{
public Type Type { get; private set; }
public TypeWrapper(Type t)
{
if (!typeof(T).IsAssignableFrom(t)) throw new Exception();
Type = t;
}
public static implicit operator TypeWrapper<T>(Type t) {
return new TypeWrapper<T>(t);
}
}
(Also implement Equals and GetHashCode, just delegate to Type.)
And then your dictionary becomes:
var d = new Dictionary<TypeWrapper<BaseClass>, int>();
d.Add(typeof(BaseClass), 2);
d.Add(typeof(Child), 3);
I believe you want to constrain type parameters to generic types - the where keyword is used for that:
class MyDict<TKey, TValue> : Dictionary<TKey, TValue> where TValue : SomeBaseClass
{
...
}
Is this what you're asking for or am I misunderstanding your question?
Edit: you cannot do exactly what you ask for in C# - you can't define an local instance of a generic type with a type constraint. You can, however, pre-declare your constrained Dictionary type (like my example) and then create an instance of that type like so:
// SomeClass will have to inherit from SomeBaseClass
MyDict<SomeClass> instance = new MyDict<SomeClass> ();
I think this is close to what you're looking for. Post a comment if I misunderstand it - I don't know Java this deep.
Was looking into this same problem and this poor man's checker is the best thing I could come up with:
class MyValue {
public Type Type { get; private set; }
private MyValue(Type type)
{
this.Type = type;
}
public MyValue of<T>() where T : BaseClass
{
return new MyValue(typeof(T));
}
}
IDictionary<int, MyValue> myDictionary = new Dictionary<int, MyValue>()
{
{ 1, MyValue.of<SubClass1>(); },
{ 2, MyValue.of<SubClass2>(); },
{ 3, MyValue.of<NotSubClass>(); }, // this causes a compile error
};