Given the class below:
public class ConcreteEmployeeRoleCreator<T, TConcrete, TSpec>
where TConcrete : class, T, new()
where T : EmployeeRole
where TSpec : EmployeeRoleSpecification
{
public ConcreteEmployeeRoleCreator(TSpec spec) { Specification = spec; }
public TSpec Specification { get; private set; }
public T Create() { return new TConcrete(); }
}
I would like to have a dictionary of 'creators', but I haven't been able to work out how to do it yet. If I try and define the dictionary using the lowest common denominator types, the compiler is not allowing an instance to be added
[Test]
public void Creator_CanCreateFromSpec() {
var creators = new Dictionary<string, ConcreteEmployeeRoleCreator<EmployeeRole, EmployeeRole, EmployeeRoleSpecification>>();
var spec = new SalesmanRoleSpecification();
var creator = new ConcreteEmployeeRoleCreator<EmployeeRole, Salesman, SalesmanRoleSpecification>(spec);
creators.Add("salesman", creator); <==== ** compile error **
}
Salesman is an EmployeeRole, and SalesmanRoleSpecification is an EmployeeRoleSpecification (or I wouldn't be able to define the creator above without a compiler error as well).
SO I guess it is the way I am declaring the dictionary? What am I doing wrong?
Cheers,
Berryl
Generic covariance has to be specified at the declaration of the covariant type - and it can only be specified for interfaces and delegates.
So, given that you're using a class, a
ConcreteEmployeeRoleCreator<EmployeeRole, Salesman, SalesmanRoleSpecification>
will never be a
ConcreteEmployeeRoleCreator<EmployeeRole, EmployeeRole, EmployeeRoleSpecification>
You'll need to look for an alternative approach. To be honest, by the time you get to three type parameters and want two of them to be covariant, you've got a pretty hard-to-understand design to start with, I'm afraid :(
Related
I am new to C#. I am trying to implement a Dictionary in C# whose Java-equivalent is:
HashMap<string, Variable<?>> dictionary
Here is the detailed Java version of what I'm trying to do: Java how to manage user-defined variables
In C# so far I have something like this:
interface IVariable { }
public class Variable<T> : IVariable
{
public T myValue { get; set; }
}
Dictionary<string, IVariable> vars = new Dictionary<string, IVariable>();
Then I try to do this:
Variable<int> age = new Variable<int>();
age.myValue = 12;
vars.Add("age", age);
IVariable theVar;
if (vars.TryGetValue("age", out theVar) {
Console.WriteLine("fetched age is " + theVar.myValue);
}
I run into trouble in the last line because the compiler doesn't recognize the myValue member of a theVar because it is an IVariable.
In this simple example maybe I could declare theVar to be a Variable<int> instead of an IVariable but I haven't tried it because it would require a priori knowledge about what kind of variable I'm fetching from the dictionary and I might not always have that knowledge.
I wouldn't mind if myValue were an inherited/abstract property (if there is such a thing), since every Variable will have a property named myValue (each will differ in type but not in name). In that case I guess I could make IVariable an abstract class rather than an interface, but then I still run into trouble as far as what to put for the type of myValue.
Could I do a cast of theVar into something using as by first checking its type with is? I'm not sure if that would work or is even possible.
I've looked at these posts for guidance (especially the second one):
Wildcard equivalent in C# generics
C# Generics: wildcards
However, my situation is still slightly different than the second example above because that example has an abstract method that is returning a void whereas I wish to have my variables return non-void generic values.
Thanks for any help.
C# has dynamic. You can create Dictionary<string, dynamic>
Or you can use object (boxing/unboxing) Dictionary<string, object>
Or you can get generic type from class
class MyClass<TDicValue>
{
Dictionary<strint, TDicValue> myDictionary;
}
I had this same problem where I had 20 slightly different types and I had to keep dictionaries on. I wanted to organize them in a list.
The problem was the same, selecting the right kind from the list with reflection or strings lacked the ability to provide a type to return to. #skrilmps answer is correct, but packing and and unpacking was at best unreliable without a lot (metric ton) of ugly messy code.
While unity does support dynamics in 2020, this doesn't exactly work with what i am doing unless I make like everything dynamic safe and that's shamble coding, not extensible or maintainable, and just sounds like a general nightmare.
I personally feel that I am an inadequate programmer after years of trying to learn and still not having my efforts provide a productive return or product of note, so i cannot claim the answer being mine, but in my research on the proper solution to this problem i found this: https://www.youtube.com/watch?v=A7qwuFnyIpM
In here he says basically if you add an interface to your similar classes that are intended for use in a variety of different lists, that you can instead make a list of that type of interface. I would assume dictionary as well, and then you can add any kind of class implementing this interface to this singular interface type defined list.
I tried using boxing/unboxing and came up with this solution. It appears to work... so far. But it doesn't seem very safe.
public interface Variable
{
object getValue();
void setValue(object value);
Type myType();
}
public class Variable<T>: Variable
{
private T myValue;
public object getValue()
{
return myValue;
}
public void setValue(object value)
{
myValue = (T)value;
}
public Type myType() { return myValue.GetType(); }
}
Dictionary<string, Variable> vars = new Dictionary<string, Variable>();
Variable<int> age = new Variable<int>();
age.setValue(21);
vars.Add("age", age);
Variable theAgeVar;
vars.TryGetValue("age", out theAgeVar);
Console.WriteLine("age = " + theAgeVar.getValue());
Variable<double> height = new Variable<double>();
height.setValue(5.9);
Variable theHeightVar;
vars.TryGetValue("age", out theHeightVar);
Debug.Log("height = " + theHeightVar.getValue());
This prints:
age = 21
height = 5.9
One thing I do not like is that I had to make the return type of getValue() be an object. If I wanted myValue (which is of type T) to implement IComparable, for instance, then this information is lost when the boxing happens and the caller receives an object.
// The following should resolve the boxing problem and now is totally generic:
public interface IVariable<T>
{
T GetContent();
void SetContent(T value);
Type GetDataType();
}
public class Variable<T> : IVariable
{
private T content;
public T GetContent()
{
return content;
}
public void SetContent(T value)
{
content = value;
}
public Type GetDataType() { return GetType(); }
}
Dictionary<string, Variable<T>> variables = new Dictionary<string, Variable<T>>();
I'm attempting to create a common interface which will allow me n methods of interacting with a database. I want my business application to be able to instantiate any of the connection methodologies and be assured the interface is identical.
Here's a simplified version of what I'm trying now.
Database Interface where IElement is another interface which would define a table.
public interface IDatabase
{
void setItem( IElement task ); //this works fine
List<T> listTasks<T>() where T : IElement; // this doesn't
}
IElement interface:
public interface IElement
{
int id { get; set; }
}
Implementation of IElement:
public class TaskElement: IElement
{
public int id { get; set; }
public string name {get; set; }
}
Implementation of IDatabase:
public class SQLiteDb: IDatabase
{
public SqLiteDb( SQLiteConnection conn )
{
database = conn;
}
public void setItem( IElement task )
{
// works fine when passed a new TaskElement() which is an implementation of IElement.
database.Insert( task );
}
//it all goes off the rails here
public List<T> listItems<T>() where T : IElement
{
var returnList = new List<IElement>
foreach (var s in database.Table<TaskElement>())
{ returnList.Add(s); }
return returnList;
}
I've tried a lot of variations on this but each one gives me a new issue. Here, for instance, there are two errors.
1)
The type arguments for method 'SQLiteDb.listTasks<T>()' cannot be inferred from the usage. Try specifying the type arguments explicitly.
2)
Cannot implicitly convert type 'System.Collections.Generic.List<TaskElement>' to 'System.Collections.Generic.List<T>'
I've tried changing the method to use an explicit type but have had issues there. If I use IElement (my generic interface for all elements )I can't return a list of TaskElement objects (my implementation of IElement) as it doesn't match the return type (List<IElement>) and if I change the return type to List<TaskElement> I'm not longer implementing the interface.
It's worth noting that I can easily get this to work if I stop using the interface and generics, but this seems like an ideal situation to use an interface. Maybe I'm trying to hard to cram a lot of stuff into an interface when another application (like direct inheritance) might be better?
Question
How can I implement an interface with a generic return value while limiting the types which can be returned to only implementations of another interface.
Let's look closely at your implementation of listItems:
public List<T> listItems<T>() where T : IElement
{
var returnList = new List<IElement>
foreach (var s in database.Table<TaskElement>())
{ returnList.Add(s); }
return returnList;
}
What you've done here is written a method where the caller is allowed to ask for any type they want in the list as long as that type implements IElement. But the code in your method doesn't give them a list of the type they want, it gives them a list of IElement. So it's violating the contract.
But the real root of your problem is database.Table<TaskElement>(). That can only ever give you instances of TaskElement. You need to make that T, but to do that you need an additional generic constraint:
public List<T> listItems<T>() where T : IElement, new
{
var returnList = new List<T>
foreach (var s in database.Table<T>())
{
returnList.Add(s);
}
return returnList;
}
This is because database.Table<T> has a new constraint, which means that it can only be given types that have a zero-parameter constructor (because that method is going to create instances of the given class).
I belive it should be something like this
public List<T> listItems<T>() where T : IElement
{
var returnList = new List<T>
foreach (var s in database.Table<T>())
{ returnList.Add(s); }
return returnList;
}
I think you are on the right track with explicitly defining your list like this:
public interface IDatabase
{
void setItem( IElement task ); //this works fine
List<IElement> listTasks<IElement>();
}
Since you can't directly cast List <TaskElement> to List <IElement> you will have to do a conversion in your listTasks method. There are several methods recommended here: Shorter syntax for casting from a List<X> to a List<Y>?. I think the Linq method is the simplest if you are ok with using Linq:
List<IElement> listOfIElement = listOfTaskElement.Cast<IElement>().ToList()
You need to use the Generic type when creating the object instance:
Instead of
var returnList = new List<IElement>();
Do this
var returnList = new List<T>();
I am trying to create an alias for a type of list of list of object. Specifically, I want to shorten all the typing I have to do for this type:
IReadOnlyList<IReadOnlyList<MyObject>>
My attempt is demonstrated here:
using System.Collections.Generic;
namespace MyApp
{
class Program
{
public class MyObject
{
public static IMyCollection GetCollection()
{
var a = new List<MyObject>();
a.Add(new MyObject());
var b = new List<IReadOnlyList<MyObject>>();
b.Add(a.AsReadOnly());
return b.AsReadOnly();
}
}
public interface IMyCollection : IReadOnlyList<IReadOnlyList<MyObject>>
{
}
static void Main(string[] args)
{
var collection = MyObject.GetCollection();
}
}
}
Unfortunately, this won't compile. There error is:
Cannot implicitly convert type
'System.Collections.ObjectModel.ReadOnlyCollection<System.Collections.Generic.IReadOnlyList<MyApp.Program.MyObject>>'
to 'MyApp.Program.IMyCollection'.
An explicit conversion exists (are you missing a cast?)
OK, so I'm close. Perhaps explicitly casting? So I change the return statement in GetCollection to
return (IMyCollection)b.AsReadOnly();
That compiles, albeit with a resharper warning: Suspicious cast: there is no type in the solution which is inherited from both 'System.Collections.ObjectModel.ReadOnlyCollection>' and 'MyApp.Program.IMyCollection'
And at runtime, I get an invalid cast exception: Unable to cast object of type 'System.Collections.ObjectModel.ReadOnlyCollection1[System.Collections.Generic.IReadOnlyList1[MyApp.Program+MyObject]]' to type 'IMyCollection'.
OK, I can accept all that. I'm the last person to ask about stuff like covariance and contravariance and stuff like that. But surely there's a way to define and create an object with a short name to stand in for a really long named datatype.
How can I create a type with a really long name and cast to a type with a really short name?
UPDATE:
A co-worker suggested using a using statement.
using IMyCollection= System.Collections.Generic.IReadOnlyList<System.Collections.Generic.IReadOnlyList<MyApp.Program.MyObject>>;
While that would work, it then becomes necessary to do that in every file that uses IMyCollection. Not exactly what I would consider a solution to my goal.
How badly do you want this?
You can manually implement your own wrapper class.
public interface IMyCollection : IReadOnlyList<IReadOnlyList<MyObject>>
{
}
public class MyCollectionImpl : IMyCollection
{
private readonly IReadOnlyList<IReadOnlyList<MyObject>> _wrappedCollection;
public MyCollectionImpl(IReadOnlyList<IReadOnlyList<MyObject>> wrappedCollection)
{
_wrappedCollection = wrappedCollection;
}
public int Count
{
get
{
return _wrappedCollection.Count;
}
}
public IReadOnlyList<MyObject> this[int index]
{
get
{
return _wrappedCollection[index];
}
}
public IEnumerator<IReadOnlyList<MyObject>> GetEnumerator()
{
return _wrappedCollection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _wrappedCollection.GetEnumerator();
}
}
Then you simply create an instance of this:
public class MyObject
{
public static IMyCollection GetCollection()
{
var a = new List<MyObject>();
a.Add(new MyObject());
var b = new List<IReadOnlyList<MyObject>>();
b.Add(a.AsReadOnly());
return new MyCollectionImpl(b.AsReadOnly());
}
}
This seems like a lot of extra work, but I would actually consider this a refactoring step.
I believe that the need to pass around types made up of complex set of generic parameters, is actually a bad smell in your code.
Ask yourself, what are you actually using IMyCollection for? Would you be able to add some specialized methods to this interface to make it easier to use?
Once you've created your own MyCollectionImpl class you can slowly add a number of methods to your IMyCollection interface to simplify it's usage. At some point, you might even get to the stage where you can stop exposing the <IReadonlyList<IReadonlyList<MyObject>> interface.
This has nothing to do with covariance. IMyCollection inherits from IReadOnlyList<IReadOnlyList<MyObject>> so you can cast an instance of IMyCollection to IReadOnlyList<IReadOnlyList<MyObject>> but not the other way around.
If you wanna have some custom conversions then you can create a type instead with the short name you want and declare a conversion from IReadOnlyList<IReadOnlyList<MyObject>> to your type using operator overloading. This really seems unnecessary and unusual way to use operator overloading but it's the only way to do what you want to achieve.
this is somewhat the same question as I've asked some time ago:
How to let a method accept two types of data as argument?
Yet the current situation differs.. a lot.
Take this:
public FormResourceSelector(Dictionary<string, Effect> resourceList, string type)
Alright, nothing wrong with it.
Now I try to run this:
FormResourceSelector frs = new FormResourceSelector(AreaEffect.EFFECTS, "Area effect");
FormResourceSelector frs2 = new FormResourceSelector(DistanceEffect.EFFECTS, "Distance effect");
Both AreaEffect and DistanceEffect (custom classes) derive from Effect.
public class AreaEffect : Effect
{
public static Dictionary<string, AreaEffect> EFFECTS = new Dictionary<string, AreaEffect>();
...
}
For some reason I get the following error while making the new FormResourceSelector instance:
Argument 1: cannot convert from 'System.Collections.Generic.Dictionary<string,SCreator.AreaEffect>' to 'System.Collections.Generic.Dictionary<string,SCreator.Effect>'
at:
new FormResourceSelector(AreaEffect.EFFECTS, "Area effect");
I suspect the dictonary being a harass, but I don't really know how to fix this.
EDIT: Easiest would be to allow input of both Dictionary and Dictionary as resourceList in the first code snippet I've given.
Why not make your class generic?
public class FormResourceSelector<T>
where T : Effect
{
public FormResourceSelector(Dictionary<string, T> resourceList, string type)
{
}
}
Too large for a comment, so here is llia's answer updated to compile:
public class FormResourceSelector<T> where T : Effect
{
// Constructor
public FormResourceSelector(
Dictionary<string, T> resourceList, string type)
{
}
}
The concept at play here is know as variance - and you've run into the issue that Dictionary doesn't support covariance, as it's not read-only - this would be unsafe, for example:
IDictionary<string, object> myDict = new Dictionary<string, string>();
myDict["hello"] = 5; // not an string
See: IDictionary<TKey, TValue> in .NET 4 not covariant
Consider the following:
public FormResourceSelector(Dictionary<string, Effect> resourceList, string type)
{
resourceList.Add(new Effect());
}
Were you to pass a dictionary of derived types in:
var effects = new Dictionary<string, AreaEffect>();
new FormResourceSelector(effects, "");
You would have issues trying to set an Effect into what is really an AreaEffect, the compiler is stopping this from happening.
You could use generics in order to specify the type at compile time, as the other answer states.
This causes a compile-time exception:
public sealed class ValidatesAttribute<T> : Attribute
{
}
[Validates<string>]
public static class StringValidation
{
}
I realize C# does not support generic attributes. However, after much Googling, I can't seem to find the reason.
Does anyone know why generic types cannot derive from Attribute? Any theories?
Well, I can't answer why it's not available, but I can confirm that it's not a CLI issue. The CLI spec doesn't mention it (as far as I can see) and if you use IL directly you can create a generic attribute. The part of the C# 3 spec that bans it - section 10.1.4 "Class base specification" doesn't give any justification.
The annotated ECMA C# 2 spec doesn't give any helpful information either, although it does provide an example of what's not allowed.
My copy of the annotated C# 3 spec should arrive tomorrow... I'll see if that gives any more information. Anyway, it's definitely a language decision rather than a runtime one.
EDIT: Answer from Eric Lippert (paraphrased): no particular reason, except to avoid complexity in both the language and compiler for a use case which doesn't add much value.
An attribute decorates a class at compile-time, but a generic class does not receive its final type information until runtime. Since the attribute can affect compilation, it has to be "complete" at compile time.
See this MSDN article for more information.
I don't know why it's not allowed, but this is one possible workaround
[AttributeUsage(AttributeTargets.Class)]
public class ClassDescriptionAttribute : Attribute
{
public ClassDescriptionAttribute(Type KeyDataType)
{
_KeyDataType = KeyDataType;
}
public Type KeyDataType
{
get { return _KeyDataType; }
}
private Type _KeyDataType;
}
[ClassDescriptionAttribute(typeof(string))]
class Program
{
....
}
This is not truly generic and you still have to write specific attribute class per type, but you may be able to use a generic base interface to code a little defensively, write lesser code than otherwise required, get benefits of polymorphism etc.
//an interface which means it can't have its own implementation.
//You might need to use extension methods on this interface for that.
public interface ValidatesAttribute<T>
{
T Value { get; } //or whatever that is
bool IsValid { get; } //etc
}
public class ValidatesStringAttribute : Attribute, ValidatesAttribute<string>
{
//...
}
public class ValidatesIntAttribute : Attribute, ValidatesAttribute<int>
{
//...
}
[ValidatesString]
public static class StringValidation
{
}
[ValidatesInt]
public static class IntValidation
{
}
Generic Attributes are available since C# 11. Now, this is possible:
[GenericAttribute<int>()]
public int Method();
However, this is not possible yet:
[GenericAttribute<T>()]
public int Method<T>(T param);
T is not known at compile time.
Also,
The type arguments must satisfy the same restrictions as the typeof
operator. Types that require metadata annotations aren't allowed. For
example, the following types aren't allowed as the type parameter:
dynamic
string? (or any nullable reference type)
(int X, int Y) (or any other tuple types using C# tuple syntax).
These types aren't directly represented in metadata. They include annotations that
describe the type. In all cases, you can use the underlying type
instead:
object for dynamic.
string instead of string?.
ValueTuple<int, int> instead of (int X, int Y).
Source: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-attributes
This is a very good question. In my experience with attributes, I think the constraint is in place because when reflecting on an attribute it would create a condition in which you would have to check for all possible type permutations: typeof(Validates<string>), typeof(Validates<SomeCustomType>), etc...
In my opinion, if a custom validation is required depending on the type, an attribute may not be the best approach.
Perhaps a validation class that takes in a SomeCustomValidationDelegate or an ISomeCustomValidator as a parameter would be a better approach.
This is not currently a C# language feature, however there is much discussion on the official C# language repo.
From some meeting notes:
Even though this would work in principle, there are bugs in most
versions of the runtime so that it wouldn't work correctly (it was
never exercised).
We need a mechanism to understand which target runtime it works on. We
need that for many things, and are currently looking at that. Until
then, we can't take it.
Candidate for a major C# version, if we can make a sufficient number
of runtime versions deal with it.
Generic attributes are supported since .NET 7 and C# 11 (in preview in .NET 6 and C# 10).
My workaround is something like this:
public class DistinctType1IdValidation : ValidationAttribute
{
private readonly DistinctValidator<Type1> validator;
public DistinctIdValidation()
{
validator = new DistinctValidator<Type1>(x=>x.Id);
}
public override bool IsValid(object value)
{
return validator.IsValid(value);
}
}
public class DistinctType2NameValidation : ValidationAttribute
{
private readonly DistinctValidator<Type2> validator;
public DistinctType2NameValidation()
{
validator = new DistinctValidator<Type2>(x=>x.Name);
}
public override bool IsValid(object value)
{
return validator.IsValid(value);
}
}
...
[DataMember, DistinctType1IdValidation ]
public Type1[] Items { get; set; }
[DataMember, DistinctType2NameValidation ]
public Type2[] Items { get; set; }