C# Working with a generic list in a method - c#

I have been teaching myself generics and I wanted to try it out with a list but I struggled upon a problem I cant figure out how to "feed" the generic list to my method. What is the proper way to make a generic method "eat" my list? :)
Heres my code:
class Program<AnyDataType>
{
static void Main(string[] args)
{
jdtlist.Add("something");
jdtlist.Add("something");
jdtlist.Add("something");
Console.WriteLine(countlist(jdtlist));
Console.ReadKey();
}
static List<AnyDataType> jdtlist = new List<AnyDataType>();
public static int countlist(List<AnyDataType> list) // Yes I know this is practically useless but thats not why I am here :)
{
int listcount = 0;
for (int i = 0; i < list.Count; i++)
{
listcount++;
}
return listcount;
}

If you are writing generic method, then it should have generic parameter
public static int CountList<T>(List<T> list)
{
int listcount = 0;
for (int i = 0; i < list.Count; i++)
listcount++;
return listcount;
}
Then you can call it with any generic list
var list = new List<AnyDataType>();
// ..
Foo.CountList(list);
Same goes to classes. If you want to parametrize class with some generic type, you should provide generic argument
public class Foo<T>
As #DStanley stated, you don't need to parametrize individual methods in that case
public class Foo<T>
{
public static int CountList(List<T> list)
{
int listcount = 0;
for (int i = 0; i < list.Count; i++)
listcount++;
return listcount;
}
}
But you need to parametrize class
Foo<int>.CountList(list)
Suggested reading: Generics (C# Programming Guide)

Your problem is not passing the list to your method - that part is fine. Your problem is that you're trying to fill a "generic" list with a "specific" type (namely strings). Making a class generic means "I am not specifying the data type here - the consumer of the class will do that". So a better use case for your class would be:
class Program
{
static void Main(string[] args)
{
MyList<string>.Add("something");
MyList<string>.Add("something");
MyList<string>.Add("something");
Console.WriteLine(MyList<string>.countlist(MyList<string>.jdtlist));
Console.ReadKey();
}
}
public class MyList<AnyDataType>
{
public static List<AnyDataType> jdtlist = new List<AnyDataType>();
public static void Add(AnyDataType item)
{
jdtList.Add(item);
}
public static int countlist(List<AnyDataType> list)
{
int listcount = 0;
for (int i = 0; i < list.Count; i++)
{
listcount++;
}
return listcount;
}
This is the minimum needed to get your program to work - there are several improvements that can be made (not using static so much, etc.) but hopefully it helps you understand generics better.

You need to call the static method of the generic class via class-name including the type of the parameter:
so instead of
Console.WriteLine(countlist(jdtlist));
this:
Console.WriteLine(Program<string>.countlist(jdtlist));
Another way is to make the method generic, not the class:
public static int countlist<AnyDataType>(List<AnyDataType> list) {}
Then you can call it in these ways(with explicit type or inferred from parameter):
Program1.countlist<string>(jdtlist)
Program1.countlist(jdtlist)

Okay you have:
public static int countlist(List<AnyDataType> list)
{
int listcount = 0;
for (int i = 0; i < list.Count; i++)
{
listcount++;
}
return listcount;
}
And well, it works fine, but only if you have List<AnyDataType> to begin with.
Well, you could do:
public static int Countlist<T>(List<T> list)
{
int listcount = 0;
for (int i = 0; i < list.Count; i++)
{
listcount++;
}
return listcount;
}
Now the method itself is generic. What's more overloads will happen automatically in that you can call CountList(new List<string>()) rather than having to explicitly call CountList<strong>(new List<string>()).
But this combines with simple matters of inheritance. Consider that your CountList could work just as well with any other IEnumerable<T> implementation:
public static int Count<T>(IEnumerable<T> source)
{
int tally = 0;
foreach(var item in source)
++tally;
return tally;
}
So just as generics mean you don't have to restrict yourself to a particular type of list, so normal inheritance and interface implementation means you can work with the most general case applicable.

Related

Primitives and IComparable interface

In Java if I wanna sort any data independent to its data type I would use Comparable interface and I found out we have similar interface in C#, so I have simple sorting algorithm:
public static void Sort(IComparable[] arr)
{
bool swap = true;
while (swap)
{
swap = false;
for (int i = 0; i < arr.Length - 1; i++)
{
if (Less(arr[i + 1], arr[i]))
{
Exchange(arr, i, i + 1);
swap = true;
}
}
}
}
So I can sort any objects that implements IComparable interface, but I can't sort any primitive datatypes and as I know C# doesn't have wrapper types for primitives like int -> Integer, but it has some structures like Int32 that actually implements IComparable interface but I still can't use it for some reason, for example:
static void Main(string[] args)
{
Int32[] ints = { 5, 2, 9, 0, 3};
BubbleSort.Sort(ints);
ReadKey();
}
I will get an error:
Error CS1503 Argument 1: cannot convert from 'int[]' to 'System.IComparable[]'
But if we check out metadata of Int32 we can see it implements IComparable
So,
What I don't know?
What's wrong here?
How can I make it better to sort any data?
An int[] isn't an IComparable[] - the latter is an array of references.
But there's a simple solution, which will also avoid boxing. Change your method declaration to be generic with a constraint on the type parameter:
public static void Sort<T>(IComparable<T>[] arr) where T : IComparable<T>
You may need to change your Exchange and Less methods as well - you haven't told us what that looks like. But they could (and probably should) be generic too. The method body of Sort shouldn't need to change, at that point.
If someone was interested:
public static class BubbleSort<T>
{
public static void Sort(T[] arr, Comparer<T> comparer = null)
{
Comparer<T> equaltyComparer = comparer ?? Comparer<T>.Default;
bool swap = true;
while (swap)
{
swap = false;
for (int i = 0; i < arr.Length - 1; i++)
{
if (Less(equaltyComparer, arr[i + 1], arr[i]))
{
Exchange(arr, i, i + 1);
swap = true;
}
}
}
}
private static bool Less(Comparer<T> comparer, T v, T w)
{
return comparer.Compare(v, w) < 0;
}
private static void Exchange(T[] arr, int i, int j)
{
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}

Generic function add element to list

I want to replace AddQuickElement and AddRangeElement with one generic function AddElement<T>. But how can i add generic element to List<Base> list. Activator do not work. Or what is better way to make this without reflection?
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
List<Base> list = new List<Base>();
AddQuickElement(list,5);
AddRangeElement(list, 5);
AddElement<Quick>(list,5);
Console.WriteLine(list.Count);
Console.ReadKey();
}
public static void AddQuickElement(List<Base> list, int number)
{
for (int i = 0; i < number; i++)
{
list.Add(new Quick());
}
}
public static void AddRangeElement (List<Base> list, int number)
{
for (int i = 0; i < number; i++)
{
list.Add(new Range());
}
}
public static void AddElement<T>(List<Base> list, int number)
{
Type type = typeof(T);
var element = Activator.CreateInstance(type);
// list.Add(element); // do not work
}
}
public abstract class Base
{
}
public class Quick:Base
{
}
public class Range : Base
{
}
}
You need to constraint the type parameter to AddElement method
public static void AddElement<T>(List<Base> list, int number) where T : Base, new()
{
for (int i = 0; i < number; i++)
{
list.Add(new T());
}
}
The type constraints where T : Base, new() mean that the type T
is derived from Base
has a public parameterless constructor.
(1) lets you add an instance of T to a List<Base>, (2) lets you create a new instance of T using new T().

Populating a list based on its element type when passed to a method

Not sure if it's possible, but I'd like to be able to populate a List<T> based on what T is. Presently, I have something like this (please forgive the generic names - it's for testing purposes):
public static class CollectionsClass
{
List<Object1> list1 = new List<Object1>();
List<Object2> list2 = new List<Object2>();
List<Object3> list3 = new List<Object3>();
}
public static class ActionClass
{
public static void PopulateCollections()
{
Populate(CollectionsClass.list1, 0, 10);
Populate(CollectionsClass.list2, 20, 50);
Populate(CollectionsClass.list3, 30, 100);
}
private static void Populate(dynamic list, int minLimit, int maxLimit)
{
var rnd = new Random();
int rndNum = rnd.Next(minLimit, maxLimit);
for (int i = 0; i < rndNum; i++)
{
if (list.GetType() == typeof(List<Object1>))
{
list.Add(new Object1());
}
else if (list.GetType() == typeof(List<Object2>))
{
list.Add(new Object2());
}
else if (list.GetType() == typeof(List<Object3>))
{
list.Add(new Object3());
}
else
{
// put out an error
}
}
}
}
While that code works, I'd like to shrink it by doing something like:
list.Add(new list.ObjectType());
I've been messing around with reflections and getting types all day, but I just can't seem to figure this one out.
Don't use dynamics, use generics:
static void Populate<T>(List<T> list, ...) where T: new()
{
...
for (int i=0; i<rndNum; i++)
list.Add(new T());
}
Since you're already using dynamic, you should be able to add method to handle this:
private static void AddToList<T>(List<T> list) where T : new()
{
list.Add(new T());
}
Given that, you can write:
private static void Populate(dynamic list, int minLimit, int maxLimit)
{
var rnd = new Random();
int rndNum = rnd.Next(minLimit, maxLimit);
for (int i = 0; i < rndNum; i++)
{
AddToList(list);
}
}
Try to use generic method:
public static class CollectionsClass
{
public static List<Object1> list1 = new List<Object1>();
public static List<Object2> list2 = new List<Object2>();
public static List<Object3> list3 = new List<Object3>();
}
public static class ActionClass
{
public static void PopulateCollections()
{
Populate(CollectionsClass.list1, 0, 10);
Populate(CollectionsClass.list2, 20, 50);
Populate(CollectionsClass.list3, 30, 100);
}
private static void Populate<T>(List<T> list, int minLimit, int maxLimit)
where T : new()
{
var rnd = new Random();
int rndNum = rnd.Next(minLimit, maxLimit);
for (int i = 0; i < rndNum; i++)
{
list.Add(new T());
}
}
}
Sounds like you want a combination of generics and reflection.
First of all make it generic:
void Populate<T>(List<T> mylist)
Now you know the type of your list: it's T.
All that is left is looping and creating instances of a particular type T. For this you can use Activator.CreateInstance:
for(int i = 0; i < 5; i++){
mylist.Add((T) Activator.CreateInstance(typeof(T)));
}
With this sample code:
void Main()
{
Populate<Type1>(new List<Type1>());
Populate<Type2>(new List<Type2>());
}
void Populate<T>(List<T> mylist){
for(int i = 0; i < 5; i++){
mylist.Add((T) Activator.CreateInstance(typeof(T)));
}
foreach(var item in mylist){
Console.WriteLine (item);
}
}
class Type1 { }
class Type2 { }
class Type3 { }
You get this output:
This will rely on reflection to create an instance of your object any assumes there is a public non-parameter constructor available (otherwise an exception will be thrown from the Activator).
This is not quite desirable behaviour and I realized it as soon as I saw the other answers that use the where T : new() constraint in their generic function: use this method over mine.
I'll still leave it in here for completeness though (at the very least it demonstrates a possible trap).
Use factory to extract the creation logic, reflection to get the correct type, and Activator to get the instance.
public static class TFactory
{
public static T Getmplementation<T>()
{
var typeName = typeof(T).Name;
var type = Type.GetType(typeName);
if (type != null)
return Activator.CreateInstance(type) as T;
else
throw new NotImplementedException(typeName);
}
}
Then,
List.Add(TFactory.GetImplmentation<'T>());
You can use a generic with a new constraint to achieve this:
private static void PopulateList<T>(List<T> list, int minLimit, int maxLimit)
where T : new()
{
var rnd = new Random();
int rndNum = rnd.Next(minLimit, maxLimit);
for (int i = 0; i < rndNum; i++)
{
list.Add(new T());
}
}
The constraint is that the type T must provide a default constructor. If you want to add items to a List, you do not need the dynamic keyword as you can specify List<T> as parameter type directly.
If you cannot add a default constructor, you can also provide a creator function:
private static void PopulateList<T>(List<T> list, Func<int, T> creatorFunc,
int minLimit, int maxLimit)
{
var rnd = new Random();
int rndNum = rnd.Next(minLimit, maxLimit);
for (int i = 0; i < rndNum; i++)
{
list.Add(creatorFunc(i));
}
}
You call the method like this:
var lst = new List<MyObjectType>();
PopulateList<MyObjectType>(lst, x => new MyObjectType(x), 1, 7);
In this sample, the value of i is provided to the creatorFunc that returns a new object of type MyObjectType.

C# reference to loop variable

Is it possible in C# to something like the following
foreach (ref string var in arr) {
var = "new value";
}
so that var variable was treated as reference and assigning to var would change an array element?
There is no such construct for updating a loop; an iterator is read-only. For example, the following provides a perfectly valid iterator:
public IEnumerable<int> Get1Thru5() {
yield return 1; yield return 2; yield return 3;
yield return 4; yield return 5;
}
How would it update? What would it update?
If the data is an array/list/etc, then something like:
for(int i = 0 ; i < arr.Length ; i++) {
arr[i] = "new value";
}
Or other options depending on the specific container.
Update; at a push, an extension method:
public static void UpdateAll<T>(this IList<T> list, Func<T, T> operation) {
for (int i = 0; i < list.Count; i++) {
list[i] = operation(list[i]);
}
}
static void Main() {
string[] arr = { "abc", "def", "ghi" };
arr.UpdateAll(s => "new value");
foreach (string s in arr) Console.WriteLine(s);
}
No. The foreach statement is simply syntax sugar on top of the IEnumerable interface. This interface defines a method to get en IEnumerator which in turn has methods to do read-only enumeration:
Current : object
MoveNext() : bool
Reset() : void
foreach(string s in strings)
{
Console.WriteLine(s);
}
is compiler shortcut for:
IEnumerator e = strings.GetEnumerator();
string s;
while(e.MoveNext())
{
s = e.Current;
Console.WriteLine(s);
}
Since IEnumerator.Current is a get-only property you can't set the value.
// Non-generic IEnumerator shown.
interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
If you want to support an updatable enumerator you will need to create it yourself -- but you won't be able to use "foreach" with it, and you'll have to implement wrappers around all the common IEnumerable classes.
You'll have to analyze your current situation and figure out how to update. If you're using an IList interface you can do:
for(int i = 0; i < strings.Count; ++i)
{
string s = strings[i];
//do work
s = s.ToUpperInvariant();
strings[i] = s;
}
In the case of a string, no; C#
strings are immutable (cannot be
changed). If you were enumerating over
objects of a different, mutable type,
you can change the properties of those
objects.
Just to illustrate what Jacob is talking about. Consider the code snippet below:
class MyInt
{
public int Val { get; set; }
public MyInt(int val) { this.Val = val; }
}
class Program
{
static void Main(string[] args)
{
MyInt[] array = new MyInt[] { new MyInt(1), new MyInt(2) };
foreach (var obj in array) Console.Write("{0}\t", obj.Val);
foreach (var obj in array)
{
obj = new MyInt(100); // This doesn't compile! the reference is read only
obj.Val *= 10; // This works just fine!
}
foreach (var obj in array) Console.Write("{0}\t", obj.Val);
}
}
Indeed, if you try to assign to the "obj" as above you'll get a compile time error. But nothing prevents you from modifying MyInt's properties through the "obj" reference
In the case of a string, no; C# strings are immutable (cannot be changed). If you were enumerating over objects of a different, mutable type, you can change the properties of those objects.

How to specify parameter for generic list type extension method in c#

I am trying to make an extension method that will shuffle the contents of a generic list collection regardless of its type however im not sure what to put in between the <..> as the parameter. do i put object? or Type? I would like to be able to use this on any List collection i have.
Thanks!
public static void Shuffle(this List<???????> source)
{
Random rnd = new Random();
for (int i = 0; i < source.Count; i++)
{
int index = rnd.Next(0, source.Count);
object o = source[0];
source.RemoveAt(0);
source.Insert(index, o);
}
}
You need to make it a generic method:
public static void Shuffle<T>(this List<T> source)
{
Random rnd = new Random();
for (int i = 0; i < source.Count; i++)
{
int index = rnd.Next(0, source.Count);
T o = source[0];
source.RemoveAt(0);
source.Insert(index, o);
}
}
That will allow it to work with any List<T>.
You just make your own method generic:
public static void Shuffle<T>(this List<T> source)
Slightly off-topic, but a Fisher-Yates shuffle will have less bias and better performance than your method:
public static void ShuffleInPlace<T>(this IList<T> source)
{
if (source == null) throw new ArgumentNullException("source");
var rng = new Random();
for (int i = 0; i < source.Count - 1; i++)
{
int j = rng.Next(i, source.Count);
T temp = source[j];
source[j] = source[i];
source[i] = temp;
}
}
I Think this solution faster to process, because you will get your itens randomly and your collection position will be preserved to future use.
namespace MyNamespace
{
public static class MyExtensions
{
public static T GetRandom<T>(this List<T> source)
{
Random rnd = new Random();
int index = rnd.Next(0, source.Count);
T o = source[index];
return o;
}
}
}
Steps:
Create your Static Class to Identify your extensions
Create you Extension Method (must be static)
Process your data.

Categories

Resources