Trying to write my first generic class in C#:
public class HighScoreList<ScoreType>
where ScoreType : System.IComparable<ScoreType>
{
...
public HighScoreList(List<ScoreType> highScoreList)
{
....
}
...
}
I have run into problems writing unit tests for it. It can't for some reason match the constructor's argument list and gives me the error:
Error 7 The best overloaded method match for 'TDGLX.FileManagement.HighScoreList.HighScoreList(System.Collections.Generic.List)' has some invalid arguments C:\Users\eric\Documents\Visual Studio 2010\Projects\TDGLX\UnitTests\FileManagmentTest\HighScoreListTest.cs 183 54 UnitTests
On this and several other tests:
HighScoreList<GenericScore> highScoreList =
new HighScoreList<GenericScore>(new List<GenericScore>()
{
new GenericScore("Person1",400),
new GenericScore("Person2",200),
new GenericScore("Person3",100)
});
HighScoreList<GenericScore> target =
new HighScoreList<GenericScore>(highScoreList);
Here is the class that I'm using as a parameter to the template argument list in my tests.
[Serializable()]
public class GenericScore : System.IComparable<GenericScore>
{
public GenericScore(string name,int score)
{
Name = name;
Score = score;
}
public string Name { get; set; }
public int Score { get; set; }
public int CompareTo(GenericScore other)
{
return this.Score.CompareTo(other.Score);
}
}
I really can't figure out what's wrong with the test. Is there something have misunderstood about C# generics?
HighScoreList<GenericScore> target =
new HighScoreList<GenericScore>(highScoreList);
In the code above, you're passing a HighScoreList<GenericScore> to the constructor of HighScoreList<GenericScore>, but it expects a List<GenericScore>
Isn't that what you want ?
List<GenericScore> highScoreList = new List<GenericScore>()
{
new GenericScore("Person1",400),
new GenericScore("Person2",200),
new GenericScore("Person3",100)
};
HighScoreList<GenericScore> target =
new HighScoreList<GenericScore>(highScoreList);
On your second call to the HighScoreList ctor, you're passing in an instance of HighScoreList<GenericScore> as the List<GenericStore> ctor argument, but HighScoreList<T> doesn't inherit off List<T>, hence the error.
As a point of style, generic type names usually start with T, so it'll be HighScoreList<TScoreType>, or just HighScoreList<T>
Related
I was looking at Xamarin code base (in this case StackLayout class) and I came with this strange line of code in C#, which I couldn't understand the syntax:
var layout = new StackLayout()
{
Children =
{ // What is this!!!?
new Label()
{
Text = "Hello",
},
new Entry()
{
Text = "Hi"
},
}
};
The code that I don't understand is the way it initializes the Children property. Children is a get-only property with no setter.
Not only it is being initialized, but also there is no new List<> before {.
Resharper can convert it to use .Add() instead of this initialization. So it seems it is not an initialization.
I think there's something added to C# which I'm not aware of!
This is just a variation of the "initializer syntax" for collections, valid when initializing property values in a new instance.
The initializer syntax in general allows assigning values to properties when using the new operator. Then, in that context, the collection initializer syntax maps an assignment to a sequence of calls to an Add() method (if present, which it is in this case).
This isn't unique to Xamarin. Here's a simple C# example:
public class Class1
{
public IList<string> List { get; } = new List<string>();
public static Class1 M()
{
return new Class1
{
List =
{
"foo", "bar"
}
};
}
}
Let's try to run your code with these classes:
public class StackLayout
{
public object[] Children;
}
public class Label
{
public string Text;
}
public class Entry
{
public string Text;
}
If I do that I get the following error:
CS1061 'object[]' does not contain a definition for 'Add' and no extension method 'Add' accepting a first argument of type 'object[]' could be found (press F4 to add a using directive or assembly reference)
If I change it to this:
public class StackLayout
{
public List<object> Children;
}
Then I get the error:
NullReferenceException: Object reference not set to an instance of an object.
So finally I do this:
public class StackLayout
{
public List<object> Children = new List<object>();
}
That works.
So the syntax you've shown is shorthand for calling an .Add(...) method.
This is a Feature of C# 6 and the collection initializer Syntax. In short: If the Item does have an Add-Method than the Compiler will use it to initialize the Collection. See Extension Add methods in collection initializers in the C# 6 release notes.
Not only for IList:
internal class Program
{
private static void Main(string[] args)
{
var p = new Person
{
Name = "Mark",
Address = //<<<<<<<< HERE
{
Number = 3,
Street = "Long street"
}
};
}
}
public class Person
{
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int Number { get; set; }
public string Street { get; set; }
}
Btw, could somebody tell me the name of this C# feature?
Sorry for a bit embarrassing question title, but I could not figure out how to describe the situation more clearly.
Let's say, I have four classes:
public class CustomModel
{
public string Value1 { get; set; }
}
public class CustomModel2 : CustomModel
{
}
public class CustomViewModel<T> where T : CustomModel
{
}
public class PageOfType<T, TT> where T : CustomViewModel<TT> where TT : CustomModel
{
public TT Model { get; set; }
public T ViewModel { get; set; }
}
So, the idea is pretty simple: I want PageOfType to have some property of a type, which is an argument of type which is an argument for itself.
So, the instantiation looks like this (which is a bit complicated and would not be so nicely used in development process):
var p0 = new PageOfType<CustomViewModel<CustomModel>, CustomModel>();
var p1 = new PageOfType<CustomViewModel<CustomModel>, CustomModel2>();
// ^^ this line gives an error as, obviously, CustomViewModel<CustomModel> and CustomViewModel<CustomModel2> are not convertable
p0.Model.Value1 = "some string"; // <- this line is perfectly what I need (it works)
So, could you give me any clue on:
how should I arrange all that "kitchen" for simply having something like this instead (in other words, not to mention CustomModel twice on initialization):
var p0 = new PageOfType<CustomViewModel<CustomModel>>();
// or even having new PageOfType<CustomViewModel2>();
// (if CustomViewModel2 is just as:
// public class CustomViewModel2 : CustomViewModel<CustomModel>
// for instance)
how can I handle the convertable error with this (as I am not sure I am able to use interfaces in this situation easily (unless I don't have another choice)):
var p1 = new PageOfType<CustomViewModel<CustomModel>, CustomModel2>();
// ^^ this line gives an error as...
You gotta use a covariant interface instead of a class: ICustomViewModel<out TModel>
http://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
I'm trying to create a collection of values that each correspond to an action. This way, I'll be able to search the collection for a particular value and then call the associated action in a generic way.
So, here was my first stab at it:
public class CommandInfo
{
public string Name { get; set; }
public Action<RunArgument> Action { get; set; }
}
public class MyClass
{
public List<CommandInfo> Commands = new List<CommandInfo>
{
new CommandInfo { Name = "abc", Action = AbcAction } // <== ERROR HERE
};
public void AbcAction(RunArgument arg)
{
; // Do something useful here
}
}
In this case, the declaration of the new CommandInfo inside the Commands collection gives me the error:
A field initializer cannot reference the non-static field, method, or property 'MyNameSpace.MyClass.AbcAction(MyNameSpace.RunArgument)'
Surely there must be a way to store a reference to a non-static method like this. Can someone help me out?
Surely there must be a way to store a reference to a non-static method like this. Can someone help me out?
There is, just not within a field initializer. So this works fine:
public List<CommandInfo> Commands = new List<CommandInfo>();
public MyClass()
{
Commands.Add(new CommandInfo { Name = "abc",
Action = AbcAction });
}
... or perform the whole assignment within the constructor. Note that this doesn't really have anything to do with delegates - it's incidental, on the basis that you're effectively referring to this.AbcAction. In every other way, it's equivalent to this problem:
public class Foo
{
int x = 10;
int y = this.x; // This has the same problem...
}
(I hope you don't really have a public field, of course...)
The problem isn't that you can't store a reference to a non-static member, it is that you cannot reference a non-static member in a field initializer. Field initializers can only reference static or constant values. Move the initialization of Commands to the constructor and it will work.
public class CommandInfo
{
public string Name { get; set; }
public Action<RunArgument> Action { get; set; }
}
public class MyClass
{
public List<CommandInfo> Commands;
public MyClass
{
Commands = new List<CommandInfo>
{
new CommandInfo { Name = "abc", Action = AbcAction }
};
}
public void AbcAction(RunArgument arg)
{
; // Do something useful here
}
}
I'd like to create an instance of a class using unity where the class has two constructors with the same number of parameters.
Here is the instantiation:
_unityContainer.Resolve<IGradeType>(new ParameterOverride("gradeTypeStringFromXmlFile", gradeTypeStringFromXmlFile));
And here are the constructors:
public GradeType(string gradeTypeStringFromXmlFile)
{
_gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile;
}
public GradeType(Enum.GradeType gradeType)
{
_gradeType = gradeType;
}
If I try to do this I get an exception saying The type GradeType has multiple constructors of length 1. Unable to disambiguate.
I can set the attribute [InjectionConstructor] over one constructor to make it work with one, but then I can't create an instance with unity using the other constructor.
Is it some way to have multiple constructors with equal number of parameters and still use unity to create the instances?
Yes it's possible to tell Unity which constructor should it use, but you can only do this when you register your type with InjectionConstructor. If you want to use both constructor it's even complicated because you have to name your registrations and use that name when resolving.
Sample built with Unity version 2.1.505:
var continer = new UnityContainer();
continer.RegisterType<IGradeType, GradeType>("stringConstructor",
new InjectionConstructor(typeof(string)));
continer.RegisterType<IGradeType, GradeType>("enumConstructor",
new InjectionConstructor(typeof(EnumGradeType)));
IGradeType stringGradeType = continer.Resolve<IGradeType>("stringContructor" ,
new DependencyOverride(typeof(string), "some string"));
IGradeType enumGradeType = continer.Resolve<IGradeType>("enumConstructor",
new DependencyOverride(typeof(EnumGradeType), EnumGradeType.Value));
An alternative option using Reflection and following the Strategy Pattern.
1) Create a base class for the constructors' arguments
public abstract class ConstructorArgs
{
}
2) Create a sequence of different concrete arguments classes:
public class StringArg : ConstructorArgs
{
public string _gradeTypeStringFromXmlFile { get; set; }
public StringArg (string gradeTypeStringFromXmlFile)
{
this._gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile ;
}
}
public class EnumArg : ConstructorArgs
{
public Enum.GradeType _gradeType { get; set; }
public EnumArg (Enum.GradeType gradeType)
{
this._gradeType = gradeType ;
}
}
3) Now in your GradeType class create the methods required for the Reflection. The ParseArguments scans the args for properties and for each one that it finds, it copies its value to the respective property of the GradeType using the SetProperty. Since it uses the property name for the matching, it is important to keep the same property name across both the GradeType and the concrete ConstructorArgs:
private void SetProperty(String propertyName, object value)
{
var property = this.GetType().GetProperty(propertyName);
if (property != null)
property.SetValue(this, value);
}
private void ParseArguments(ConstructorArgs args)
{
var properties = args.GetType().GetProperties();
foreach (PropertyInfo propertyInfo in properties)
{
this.SetProperty(propertyInfo.Name,
args.GetType().GetProperty(propertyInfo.Name).GetValue(args));
}
}
4) In your GradeType class create the respective properties (mind that you must use exactly the same names and types that you used in the concrete ConstructorArgs but you can use any access modifiers you like)
public string _gradeTypeStringFromXmlFile { get; set; }
public Enum.GradeType _gradeType { get; set; }
5) Create a constructor for your GradeType class with a parameter of type ConstructorArgs:
public GradeType(ConstructorArgs args)
{
this.ParseArguments(args);
}
6) Now you can register the GradeType in Unity using a single constructor but you can pass in different types as arguments when resolving it:
_unityContainer.RegisterType<IGradeType, GradeType>(
new InjectionConstructor( typeof(ConstructorArgs) ));
var args1 = new StringArg(gradeTypeStringFromXmlFile); // string
IGradeType gradeType1 = _unityContainer.Resolve<IGradeType>(
new ResolverOverride[]{new ParameterOverride("args", args1)});
var args2 = new EnumArg(gradeType); // enum
IGradeType gradeType2 = _unityContainer.Resolve<IGradeType>(
new ResolverOverride[]{new ParameterOverride("args", args2)});
If you are planning to repeatedly resolve your type in an iteration that approach might not be ideal, since Reflection comes with a performance penalty.
Remove one constructor, and cast the string to the enum, or vice-versa, and then resolve using the container.
I have a list where i want the type to be definined by the call for example in the class i want something like
public class ClassName
{
public ListType<T> ListName { get; set; }
// other class items
}
and then the usage to be what sets the class type like
var className = new ClassName()
{
ListType<int> = data
};
so basically thats what i want, i have it working using dynamic so the class is
public class ClassName
{
public ListType<dynamic> ListName { get; set; }
// other class items
}
and the call is
var className = new ClassName()
{
ListType<dynamic> = data
};
this works but i would like to know if there is a better way to do this so i dont have to use dynamic
oh almost forgot to mention the ListType is
public class ListType<T> : List<T>
{
}
and so doesnt fail by having different types passed to it
thanks
edit:
realised my usage of the code on stack overflow differed from my code
the ListType has a constructor that takes 3 arguments so the usage is more
var className = new ClassName()
{
ListName = new ListType<Type>(x, y, z)
}
How about
public class ClassName<T>
{
public ListType<T> ListName { get; set; }
// other class items
}
then use it like this:
var className = new ClassName<int>()
{
ListName = data;
};
Slight addition to Bertrand's answer gives you a way to not repeat the type argument in you specific use case, or even not mention it:
public static class ClassName
{
public static ClassName<T> Create<T>(ListType<T> list)
{
return new ClassName<T> { ListName = list };
}
public static ClassName<T> Create<T>(params T[] list)
{
return new ClassName<T> { ListName = new ListType<T>(list) };
}
}
Using the first method, you can write something like
ClassName.Create(new ListType<SomeType>(x, y, z));
using the second method, you can even write
ClassName.Create(x, y, z);
and let the compiler figure out that T is SomeType, but that doesn't work always.
Note that ClassName is different class than ClassName<T> and you might want to name it differently, e.g. ClassNameFactory.