C# Generics and inferring type - c#

I'm struggling while trying to write generic methods to save and load data from a Sterling database.
This is my save method:
public static void SaveList<T>(List<T> listToSave)
{
foreach (T listItem in listToSave)
{
DatabaseInstance.Save(listItem);
}
}
I get squiggly red line under save and an error of "The Type T must be a reference type in order to use it as parameter T"
This is my load method:
public static List<T> LoadList<T>()
{
List<T> list = (from index in DatabaseInstance.Query<T, int>() select index.Key).ToList();
return list;
}
I get the same error.
Any ideas?
Cheers
Steve
UPDATE:
I added a where T : class as suggested and got an error:
The Type T must have a public parameterless constructor in order to use it as a parameter
Following instructions in the link provided by Bryan I added , new() on the end and all working now.

You need a generic type constraint on your definition:
public static void SaveList<T>(List<T> listToSave) where T : class
{
foreach (T listItem in listToSave)
{
DatabaseInstance.Save(listItem);
}
}
public static List<T> LoadList<T>() where T : class
{
List<T> list = (from index in DatabaseInstance.Query<T, int>() select index.Key).ToList();
return list;
}

To fix your code, you need a generic constraint that forces a reference type, as shown below. To set this answer out from others already posted, I also recommend that you use IEnumerable<T> rather than List<T>:
public static void SaveList<T>(IEnumerable<T> itemsToSave) where T : class
{
foreach (T item in itemsToSave)
{
DatabaseInstance.Save(listItem);
}
}
public static IEnumerable<T> LoadList<T>() where T : class
{
return (from index in DatabaseInstance.Query<T, int>()
select index.Key);
}
The change to IEnumerable<T> should be compatible with all your existing code, as List<T> already implements IEnumerable<T>. Used properly, this will also allow you get a nice performance boost by keeping fewer items at a time in RAM and make your code more powerful by allowing it to work with other collection types.

The problem is that, in your existing code, T can be anything. That's not OK because the Save function only accepts reference types.
You need to place a constraint which effectively makes a commitment to the compiler that T will be a reference type.
public static void SaveList<T>(List<T> listToSave) where T : class
{
foreach (T listItem in listToSave)
{
DatabaseInstance.Save(listItem);
}
}

can you try?
public static void SaveList<T>(List<T> listToSave) where T:class
{
foreach (T listItem in listToSave)
{
DatabaseInstance.Save(listItem);
}
}

Try adding a class constraint:
public static void SaveList<T>(List<T> listToSave) where T : class
{
foreach (T listItem in listToSave)
{
DatabaseInstance.Save(listItem);
}
}
public static List<T> LoadList<T>() where T : class
{
List<T> list = (from index in DatabaseInstance.Query<T, int>()
select index.Key).ToList();
return list;
}

Related

C# Type as object with indexer

Consider a situation:
I have a method which use DataRow:
public void MyMethod (DataRow input)
{
DoSomething(input["Name1"]);
}
But now I have some another input types with indexer which I want to pass to this method. St like:
public void MyMethod (AnyTypeWithIndexer input)
{
DoSomething(input["Name1"]);
}
But I haven't found anything like that. I tried IDictionary but it didn't work.
Is there any super type st like "Indexable" or anything with which I can replace the "AnyTypeWithIndexer"?
Note: I still need this method to pass the DataRow and also my custom class (which I want to implement).
Can anybody help?
Thanks.
No, unfortunately, there is no interface that automatically applies to "all classes with an indexer that takes a string argument and returns an object".
What you can do, however, is to create a "proxy class" that implements such an interface
yourself:
public interface IStringToObjectIndexable
{
object this[string index] { get; set; }
}
class DataRowWrapper : IStringToObjectIndexable
{
private readonly DataRow row;
public DataRowWrapper(DataRow row) => this.row = row;
public object this[string index]
{
get => row[index];
set => row[index] = value;
}
}
MyMethod can now be declared as follows:
public void MyMethod(IStringToObjectIndexable input)
{
DoSomething(input["Name1"]);
}
// Compatibility overload
public void MyMethod(DataRow input) => MyMethod(new DataRowWrapper(input));
You can use dynamic type, but you will need to be noticed about the disadvantages of dynamic, such as performance drawbacks because of DLR, and the fact that type safety should be on your shoulders
public class WithIndexer
{
public int this[string key] => 42;
}
public static async Task Main()
{
Dictionary<string, int> a = new Dictionary<string, int>();
a.Add("meow", 41);
Foo(a, "meow");
Foo(new WithIndexer(), "key");
}
private static void Foo(dynamic indexed, string key)
{
Console.WriteLine(indexed[key]);
}
Output:
41
42

Is it possible to extend List<T> but only for T = exact type?

I'm experimenting with extending classes and managed to extend List<T> for fun like so:
public static void SomeCustomSort<T>(this List<T> list, string item)
{
if (typeof(T) != typeof(string) || list.Count == 0)
return;
// doStuff();
}
I wondered if there was a smarter way to extend List<T> only for List<string> so that my extension method is not listed or accessable for any other type T
Just make your method non-generic:
public static void SomeCustomSort(this List<string> list, string item)
and specify exact type it should work with
NOTE: With void methods even if you want to restrict extension method parameter to some set of types (e.g. all implementors of some interface or some non-sealed class with classes derived from it) I would not recommend using generic method with parameter constraint:
public static void SomeCustomSort<T>(this List<T> animals)
where T: IAnimal
Why? Because it overcomplicates your code. Non-generic method is more simple to understand than generic method. Generic method without constraint is more simple to understand than generic method with constraint. You should start from the simplest solution which is easy to understand. What sounds more natural to you?
"It sorts list of animals"
"It sorts list of items of any type"
"It sorts list of items of any type which is animal"
When to use generic type constraint? When you return items from your method and you don't want to lose information about the exact type of list items. Consider method which returns animals by some weight filter
public static IEnumerable<IAnimal> WhereWeightBelow(this List<IAnimal> animals, int weight)
If you'll pass list of dogs to this method, you will lose intellisense for all dog-specific information in the method output.
dogs.WhereWeightBelow(10).Where(d => d. /* oops only IAnimal members here */)
Returning generic type will preserve all dog info for you.
Another alternative not yet mentioned:
public static void SomeCustomSort<T>(this List<T> list, string item)
where T: YourSpecificType
This allows you to specify more than just one type, for example:
public static void SomeCustomSort<T>(this List<T> list, string item)
where T: ISortable, ICustomInterface
Just specify T instead of making it a generic method.
public static void SomeCustomSort(this List<string> list, string item)
Just define exactly string type on your extension method
public static void SomeCustomSort(this List<string> list, string item)
{
// doStuff();
}
You can also use a constraint like this (in this example T would have to be of type Project):
public static void SomeCustomSort<T>(this List<T> list, string item)
where T : Project
{
}
I would show you in the following example how you can easily expand a generic list.
I expanded the list to return random data from the list itself.
We have a class for example:
public class ExampleClass
{
public string Name { get; set; }
}
We have now made a list of these classes in some method:
var exampleList = new List<ExampleClass>()
{
new ExampleClass()
{
Name = "Class1"
},
new ExampleClass()
{
Name = "Class2"
},
new ExampleClass()
{
Name = "Class3"
}
};
var randomList = exampleList.Random(2);
The following is a simple implementation of returning random objects from a list
public static class ListExtensions
{
public static IList<T> Random<T>(this IList<T> list, int numberOfResult) where T : class
{
if (list == null) throw new ArgumentNullException(nameof(list));
if (numberOfResult <= 0 || numberOfResult > list.Count) throw new ArgumentOutOfRangeException(nameof(numberOfResult));
var random = new Random();
var randomList = new List<T>();
var randomNumbers = new List<int>();
while (randomList.Count < numberOfResult)
{
var index = random.Next(list.Count);
if (randomNumbers.IndexOf(index) < 0)
{
randomNumbers.Add(index);
randomList.Add(list[index]);
}
}
return randomList;
}
}

defining a generic action that takes a generic action as parameter

I am using .Net 4.5. How do I define a generci action that takes in another generic action as a parameter?
When I try the following, the compiler seems to interpret T as an actual type and complains that its definitions is not found:
public static Action<Action<T>, IEnumerable<T>> RepeatForEach = (a, x) =>
{
foreach (T t in x)
{
a.Invoke(t);
}
};
I tried the following, none of which worked:
Action<T><Action<T>>
Action<Action<T>><T>
Action<Action<T>> where T : object
I dont think it is possible to create a generic type based upon another generic. As said by sstan:
"Even generic types need to be declared somewhere."
Instead you should try another approach, what about you create an extention method instead? This is my sugestion:
public static void RepeatForEach(this Action<T> action, IEnumerable<T> itens)
{
foreach (T t in x)
{
a.Invoke(t);
}
}
So you can call it by:
Action<int> t = (int i)=> Console.WriteLine(i);
t.RepeatForEach(myitens)
I'm afraid that C# doesn't support this level of genericity. You will have to make the enclosing scope generic.
Either have a class:
public static class RepeatForEach<T>
{
public static Action<Action<T>, IEnumerable<T>> Action = (action, enumerable)
{
foreach (var element in enumerable)
{
action.Invoke(element);
}
}
}
Or have a method returning the action:
public static class MyClass
{
public static Action<Action<T>, IEnumerable<T>> GetRepeatForEachAction<T>()
{
return (action, enumerable) =>
{
foreach (var element in enumerable)
{
action.Invoke(element);
}
}
}
}
I'd also like to put forth an idea that having something do foreach on IEnumerable is generally a very bad idea. The reason for that would be to achieve side effects and IEnumerable is not built for that.
Imagine having an IEnumerable created via yield return. Then your action might not affect any of the elements (as those may be thrown away and recreated upon enumeration).

LINQ where doesn't accept 1 or 2 parameters

The following code with a boolean parameter works pretty well:
public List<T> SearchByStatus(bool status, List<T> list)
{
return (List<T>)list.Where(_item => _item.Executed == status);
}
But if I want to use something like this
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return (List<T>)list.Where(_item => _item.CodeType == codeType);
}
, the IDE throws an error saying Func<T, int, bool> doesn't accept 1 parameter.
I researched a bit and found for example this.
If I now add a seond parameter, lets say
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return (List<T>)list.Where((_item, _index) => _item.CodeType == codeType);
}
it says Func<T, bool> doens't accept 2 parameters.
The messages itself are correct, but I don't get why it assumes I want to use the overloaded version of Where in the first case and the non-overloaded in the second... Am I doing something wrong?
P.S.: The ECodes-type used is defined as
public enum ECodes : int
{
....
}
May that cause the issue?
Both of these should work fine:
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return list.Where((_item, _index) => _item.CodeType == codeType).ToList();
}
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return list.Where(_item => _item.CodeType == codeType).ToList();
}
If they don't - please check whether you have using System.Linq; at the top, and are using regular LINQ (not something obscure like LINQBridge).
You could also use:
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return list.FindAll(_item => _item.CodeType == codeType);
}
Note that all of this assumes that you have a suitable generic constraint on T such that T.CodeType is well-defined - presumably:
class Foo<T> where T : IHazCodeType
{
List<T> SearchByCodeType(ECodes codeType, List<T> list) {...}
}
interface IHazCodeType
{
ECodes CodeType {get;}
}

Calling a Delegate of an instance method on a Generic

I'm not having much luck searching for this answer, as I think that I don't know enough about the proper terms for it.
(Edit for clarity of code and how I call it)
I have a class that instantiates an extension method:
public static class Foo
{
public static IList<T> Bar<T>(this DataTable table) where T : class, new()
{
IList<T> list = ... // do something with DataTable.
return list;
}
}
I call this method like this:
DataTable table = SomehowGetTable();
IList<MyObject> list = table.Bar<MyObject>();
Again, greatly simplified.
Now, what I'd like to do, is add a delegate(?) so that after I get the list, I can call a method on each T in the list, like so:
public static class Foo
{
public static IList<T> Bar<T>(this DataTable table, MyDelegate postProcess)
{
IList<T> list = ... // do something with DataTable.
foreach(T item in list)
{
t.postProcess();
}
return list;
}
}
I don't know what I need to be able to pass in postProcess.. a delegate, a Predicate, or an Action, mostly because I'm Linq-challenged still.
Or, maybe this isn't even possible?
What you want is an Action< T > so you can do:
public static class Foo
{
public static IList<T> Bar(this DataTable table, Action<T> postProcess)
{
IList<T> list = ... // do something with DataTable.
foreach(T item in list)
{
postProcess(item);
}
return list;
}
}
First, you need to define Bar as generic, otherwise it won't compile.
Second, if you're trying to operate on each element within the list, you need to pass in a delegate that takes a single parameter of type T and returns no value. A built-in .NET delegate type is Action<T>, and it'll do fine.
So,
public static IList<T> Bar<T>(this DataTable table, Action<T> postProcess)
{
IList<T> list = ... // do something with DataTable
foreach(T item in list)
{
postProcess(item);
}
return list;
}
The signature would be
public static IList<T> Bar<T>(this DataTable table, Action<T> postProcess) {
var list = // Get ILIst<T> from DataTable
foreach (var i in list)
postProcess(i);
}
These days .Net brings virtually all method signatures to the table that you may ever need through Action and Func delegates. While action covers all void return type methods, Func introduces non-void returns.
Note that T must be defined as type argument on your method. The compiler may be able to infer T from the action you provide into the method:
List<double> myDoubles = table.Bar((double x) => Debug.Writeline(x));
For example if you are actually processing the values coming into a different type, the signature may look like :
public static IList<Z> Bar<T,Z>(this DataTable table, Func<T,Z> postProcess) {
return /* Get Listof T */ .Select(postProcess).ToList();
}
Used like
List<int> values = table.Bar((double d) => (int)d);
You can do either
public static IList<T> Bar<T>(this DataTable table, Action<T> postProcess)
{
...
postProcess(someT);
...
}
or add a generic constraint:
public static IList<T> Bar<T>(this DataTable table)
where T : IHasPostProcess
{
...
someT.postProcess();
...
}
Try Action<T>:
public static class Foo
{
public static IList<T> Bar(this DataTable table, Action<T> postProcess)
{
IList<T> list = ... // do something with DataTable.
foreach(T item in list)
{
postProcess(item);
}
return list;
}
}
Or use an interface so you do not have to pass action: (I prefer this one)
public interface IDo
{
Do();
}
public static class Foo
{
public static IList<T> Bar(this DataTable table) where T : IDo
{
IList<T> list = ... // do something with DataTable.
foreach(T item in list)
{
item.Do();
}
return list;
}
}

Categories