I am trying to send a params object[i] to a T function but I cant find the right syntax.
Here is an example to show the context of what I am trying to achieve:
private bool decodeValue<T>(int id,ref T item, string code)
{
if(!TypeDescriptor.GetConverter (item).CanConvertFrom(typeof(string)))
{
errorThrow (id + 2);
return false;
}
try
{
item = ((T)TypeDescriptor.GetConverter (item).ConvertFromString (code));
}
catch
{
errorThrow (id + 2);
//item = default(T);
return false;
}
return true;
}
private bool decodeValues(string[] code, int id, params object[] items)
{
for (int i = 0; i < items.Length; i++)
{
System.Type t = items [i].GetType();
//in this part i cant find the correct syntax
if(decodeValue<(t)items[i]>(id, ref items[i] as t, code[i+2]))
{
}
}
return false;
}
At the line decodeValue<(t)items[i]>(id, ref items[i] as t, code[i+2]
no matter what syntax I try, the compiler tells me that after > it expects a )
I could inline the function decodeValue into the for loop but I think there is a more elegant way of doing it. Any suggestions?
In your example, you replace content of items. Why do even need templated function? Just do this:
private bool decodeValue(int id,ref object item, string code)
{
if(!TypeDescriptor.GetConverter(item).CanConvertFrom(typeof(string)))
{
errorThrow (id + 2);
return false;
}
try
{
item = TypeDescriptor.GetConverter(item).ConvertFromString(code);
}
catch
{
errorThrow (id + 2);
return false;
}
return true;
}
private bool decodeValues(string[] code, int id, params object[] items)
{
for (int i = 0; i < items.Length; i++)
{
//in this part i cant find the correct syntax
if(decodeValue(id, ref items[i], code[i+2]))
{
}
}
return false;
}
If you ever need type of item in your code, just call .GetType() where it actualy needed. That is not only good way to do things, but in some cases performance effective (compiler can greatly optimise some calls of this function).
generics (<T>) and reflection (.GetType()) are not good friends. There is no convenient way to express that. You can, however, abuse dynamic to do it, but you can't easily use ref in that case. Further, you can't ref between an object and a T. So basically, there's a lot of hurdles. Frankly, I think you should assess whether decodeValue<T> really needs to be generic. In this scenario - especially when using TypeDescriptor - I very much doubt that it needs to be. I suspect your best bet would be to have:
private bool DecodeValue(int id, ref object item, Type type, string code)
The generics' syntax if for compile time while GetType() is for runtime. You cannot mix them of use them together.
A generic class is resolved while you source is converted to binary. The type or interface provided in a generic type is used in that area.
On the contrary GetType returns a type during execution. That type cannot be inserted in a generic definition because compilation has already be done at that time.
Related
I have many methods which are very similar as shown in the code below:
public static void ReadFromKeyboard(string label, out int retVal)
{
try
{
Console.Write(label);
retVal = int.Parse(Console.ReadLine());
}
catch (Exception)
{
Console.WriteLine("Please insert int value.");
ReadFromKeyboard(label, out retVal);
}
}
public static void ReadFromKeyboard(string label, out float retVal)
{
try
{
Console.Write(label);
retVal = float.Parse(Console.ReadLine());
}
catch (Exception)
{
Console.WriteLine("Please insert float value.");
ReadFromKeyboard(label, out retVal);
}
}
public static void ReadFromKeyboard(string label, out double retVal)
{
try
{
Console.Write(label);
retVal = double.Parse(Console.ReadLine());
}
catch (Exception)
{
Console.WriteLine("Please insert double value.");
ReadFromKeyboard(label, out retVal);
}
}
By the other hand, I don't know which method I will call. I'll discorver it only at runtime.
Is there any way I could rewrite these many methods into a single method named something like "ReadFromKeyboard" which returns either an int, a float or a double depending on the type which is passed to it as a parameter?
Thank you!
As other answers have shown, you can eliminate the duplicated code by a variety of techniques, all of which are horrible and you should not do them.
In particular, do not attempt to use generics to solve this "problem". Generics are for situations where the code is generic. That is why they are called generics! That is, the code operates the same on every possible type. Your example is the opposite of generic code; you have different rules for a small number of types, and the way to handle that situation is to do exactly what you have already done: implement one method per different rule.
I say "problem" in quotes because you do not actually have a problem to solve here, so stop trying to solve it. Writing half a dozen similar short methods is not a major burden on authors or maintainers.
Now, that said, your code is also not as good as it could be and you should rewrite it. The correct way to write your code is:
public static int ReadInteger(string label)
{
while(true)
{
int value;
Console.Write(label);
string read = Console.ReadLine();
bool success = int.TryParse(read, out value);
if (success)
return value;
Console.WriteLine("Please type an integer value.");
}
}
The problems with your original implementation are:
Do not use exception handling as mainline control flow. Do not catch an exception if the exception can be avoided. That's what TryParse is for.
Do not use recursion as unbounded looping. If you want an unbounded loop, that's what while(true) is for. Remember, C# is not tail recursive by default!
Do not use out parameters without need. The method logically returns an integer, so actually return an integer. Rename it so that you do not get collisions with other read methods. There is no compelling benefit to making the caller write Read<int> over ReadInteger, and many compelling benefits to avoiding the out param.
I've tried to implement the code according to Eric Lippert recipes. The code below
does not use exception handling as mainline control flow
does not use recursion at all
does not use output parameters without need
.
private static void Main(string[] args)
{
int intValue = ReadFromKeyboardInt32("enter int");
float floatValue = ReadFromKeyboardSingle("enter float");
double doubleValue = ReadFromKeyboardDouble("enter double");
Console.WriteLine($"{intValue}, {floatValue}, {doubleValue}");
}
public static Double ReadFromKeyboardDouble(string label) =>
ReadFromKeyboard(label, (text) => (Double.TryParse(text, out var value), value));
public static Int32 ReadFromKeyboardInt32(string label) =>
ReadFromKeyboard(label, (text) => (Int32.TryParse(text, out var value), value));
public static Single ReadFromKeyboardSingle(string label) =>
ReadFromKeyboard(label, (text) => (Single.TryParse(text, out var value), value));
public static T ReadFromKeyboard<T>(string label, Func<string, (bool, T)> tryParse)
{
for (; ; )
{
Console.Write($"{label}: ");
var result = tryParse(Console.ReadLine());
if (result.Item1)
{
return result.Item2;
}
Console.WriteLine($"Please enter valid {typeof(T).Name} value");
}
}
Instead of listing all the possible types (which you might not know beforehand), it is possible to use the System.Convert class, specially the Convert.ChangeType() method. As a proof of concept you can use a method like this:
public static void ReadFromKeyboard<T>(string label, out T result) {
Type targetType = typeof(T);
Console.Write($"{label}: ");
string input = Console.ReadLine();
object convertedValue = Convert.ChangeType(input, targetType);
result = (T)convertedValue;
}
You can use this method like this:
public static void Main(string[] args) {
ReadFromKeyboard("enter a double", out double d);
ReadFromKeyboard("enter an int", out int i);
Console.WriteLine($"double: {d}");
Console.WriteLine($"int: {i}");
}
This way it is possible to use any type you want (assuming it is supported by the Convert class). Obviously you can add exception handling and a do-while loop in the ReadFromKeyboard method if you like.
If you want to rely on overload resolution for the runtime to decide which method to call, then you must have a separate method for each type you will support. That's how it works.
On the other hand, if you can allow the user to supply at least a little type information, we can improve things a bit with generics by removing try/catch and using a real return statement. You'd call it like this:
var myNumber = ReadFromKeyboard<double>("Enter a double: ");
And the code would look like this:
public static T ReadFromKeyboard<T>(string label, int maxRetries = int.MaxValue)
{
while (maxRetries >= 0)
{
Console.Write(label);
if (typeof(T) == typeof(int))
{
int result;
if (int.TryParse(Console.ReadLine(), out result)) return (T)(object)result;
}
if (typeof(T) == typeof(float))
{
float result;
if (float.TryParse(Console.ReadLine(), out result)) return (T)(object)result;
}
else if (typeof(T) == typeof(double))
{
double result;
if (double.TryParse(Console.ReadLine(), out result)) return (T)(object)result;
}
else if (typeof(T) == typeof(decimal))
{
decimal result;
if (decimal.TryParse(Console.ReadLine(), out result)) return (T)(object)result;
}
else
throw new InvalidOperationException("Unsupported type");
maxRetries--;
}
throw new InvalidOperationException("Too many bad inputs");
}
But you have to do some really janky casting and type checking to make it work. There is still a potential this can throw an exception, which it seems like you want to avoid, but if your user sits there for more than 2 billion attempts, I doubt they'll be very surprised.
Can a 'generic type' be an array?
And in the cases where it is, how can one access that array?
Can you access a given generic type T as an array, when it is one, and as a non-array when it is not one?
For instance:
If I had a method like ->
void MustBeOfType<T>(T value){}
Can I have ->
MustBeOfType<int>(10);
And Also ->
MustBeOfType<int[]>( new int[] { 1, 2, 3 } );
And within those generic methods, can they access the values as one would expect? - one as an int and one as an int[]?
I think there might be something with typeof(T).IsArray()... but I just can't for the life of me figure out how to cast the parameter as an array when it is one.
Thanks!
You could...but, I'm not sure you should:
void MustBeOfType<T>(T value)
{
Array array = value as Array;
if (array != null) //It is an array
{
foreach (var arrayItem in array)
{
}
for (int i = 0; i < array.Length; i++)
{
var arrayItem = array.GetValue(i);
}
}
else //It is not an array
{
}
}
I am not sure what you are trying to do but Generic types can be anything. You can put restrictions to your generic type with where clause, but this is up to you and up to functionality and context.
Lets take List as example. Let say that we have List inside List. Then we define it as:
List<List<string>> myListOfList = new List<List<string>>();
your must be of type can also be anything ( if you didnt put restriction with where clause)
MustBeOfType<int[][]>()
MustBeOfType<List<List<string>>>()
MustBeOfType<AnyOtherGenericClass<List<string>>>()
and to be able to access it:
class MustBeOfType<T>
{
private T _value;
MustBeofType(T value)
{
_value = value;
}
}
to be able to make operation on , you can use reflection or if you put where restriction and your where restriction has Type, then you can see properties of that Type.
I have an array of objects (object[]). All the items in this array have the same type (unknown at compile time). I need to convert this array in a typed array, that is, if the items are integers, I want to get an int[].
I've looked into the Array.ConvertAll method, but I need to specify a specific type in the template, meaning that I have to get the element type then call ConvertAll for each case possible.
I've also looked into the keyword dynamic with no more luck (looks like dynamic[] is the same as object[]).
How can I achieve this?
It sounds like you want something like:
dynamic array = Array.CreateInstance(input[0].GetType(), input.Length);
for (int i = 0; i < input.Length; i++)
{
array[i] = (dynamic) input[i];
}
Here the dynamic just handles the conversion part for you.
Alternatively:
public static Array ConvertArray(object[] input)
{
dynamic sample = input[0]; // Just used for type inference
return ConvertArrayImpl(sample, input);
}
private static T[] ConvertArrayImpl<T>(T sample, object[] input)
{
return input.Cast<T>().ToArray();
}
You could do make the ConvertArrayImpl call with reflection manually of course, instead of using dynamic typing.
Also note that all of these will fail if the input array is empty...
Similar to Jon's solution you can do without dynamic and make use of the Array type:
public Array Convert(Array a) {
if (a.GetLength(0) == 0){
return new int[0];
}
var type = a.GetValue(0).GetType();
var result = Array.CreateInstance(type, a.GetLength(0));
for (int i = 0; i < a.GetLength(0); i++) {
result.SetValue(a.GetValue(i), i);
}
return result;
}
I am trying to create a Collection with properties and their respective accessors.
Here is my code:
class SongCollection : List<Song>
{
private string playedCount;
private int totalLength;
public string PlayedCount
{
get
{
foreach (Song s in this)
{
if (s.TimesPlayed > 0)
{
return s.ToString();
}
}
}
}
public int TotalLength
{
get
{
foreach (Song s in this)
{
int total = 0;
total += s.LengthInSeconds;
}
return total;
}
}
}
I'm receiving the error at the "get" point. It tells me that not all code paths return a value... What exactly does this mean, and what am I missing?
Firstly, the reason you're getting that message is that if this is empty, then the code within the foreach block (which is where the required return statement is) would never be executed.
However, your TotalLength() function would always return the length of the first Song, as you're declaring your variable, setting its value, then returning it within the foreach block. Instead, you'd need to do something like this:
int totalLength = 0;
foreach(Song s in this)
{
total += s.LengthInSeconds;
}
return totalLength;
Your PlayedCount function suffers from similar issues (if the collection is empty or contains no elements whose TimesPlayed property is greater than 0, then there would be no way for it to return a value), so judging by your comment you could write it this way:
public int PlayedCount()
{
int total = 0;
foreach(Song s in this)
{
if (s.TimesPlayed > 0)
{
total++;
}
}
return total;
}
It means just as it says, not all code paths return a value.
In this case, if your list is empty, then it cannot call return. In a foreach, there must be at least one item for the code to execute. Now, maybe you know that the list will always contain a value, but the compiler can't know that
What would your method return if this did not evaluate?
if (s.TimesPlayed > 0)
{
return s.ToString();
}
try using an else to return an empty string or something
The fact that 'this' could have no songs- in that case the loops will not execute at all and there is no implicit return value in C#.
Furthermore, your getters don't really make sense unless you only ever had one song in the collection. You need something more like this:
public int TotalLength()
{
get
{
int total = 0;
foreach (Song s in this)
{
total += s.LengthInSeconds;
}
return total;
}
}
Finally, without knowing how you keep track of TimesPlayed for each individual song, I wouldn't know how to implement that getter, but I am sure you can figure it out with this much.
I would like to convert T to T[] if it is an array.
static T GenericFunction<T>(T t)
{
if (t == null) return default(T);
if (t.GetType().IsArray)
{
//if object is an array it should be handled
//by an array method
return (T) GenericArrayFunction((T[])t);
}
...
}
static T[] GenericArrayFunction<T>(T[] t)
{
if (t == null) return default(T);
for (int i = 0 ; i < t.Length ; i++)
{
//for each element in array carry
//out Generic Function
if (t[i].GetType().IsArray())
{
newList[i] = GenericArrayFunction((T[])t[i]);
}
else
{
newList[i] = GenericFunction(t[i]);
}
}
...
}
Error If I try (T[])t
Cannot convert type 'T' to 'T[]'
Error If I just try to pass t
The type arguments for method 'GenericArrayFunction(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Judging from your particular example, could you not define two methods and let the compiler choose the correct one when an array is passed in?
using System;
class Program
{
static T GenericFunction<T>(T t)
{
Console.WriteLine("GenericFunction<T>(T)");
return default(T);
}
static T[] GenericFunction<T>(T[] t)
{
// Call the non-array function
for(int i = 0; i < t.Length; ++i)
t[i] = GenericFunction(t[i]);
Console.WriteLine("GenericFunction<T>(T[])");
return new T[4];
}
static void Main()
{
int[] arr = {1,2,3};
int i = 42;
GenericFunction(i); // Calls non-array version
GenericFunction(arr); // Calls array version
}
}
Just because T is an array type doesn't mean that it's also an array of T. In fact, the only way that could happen would be for T to be something like object, Array or one of the interfaces implemented by arrays.
What are you really trying to do? I suspect you want to find out the element type of the array, and then call GenericArrayFunction with the appropriate T - but that won't be the same T, and you'll need to call it with reflection, which will be somewhat painful. (Not too bad, but unpleasant.)
I suspect you don't fully understand C#/.NET generics - please give us more context about the bigger picture so we can help you better.
EDIT: The reflection approach would be something like this:
private static readonly ArrayMethod = typeof(NameOfContainingType)
.GetMethod("GenericArrayFunction", BindingFlags.Static | BindingFlags.NonPublic);
...
static T GenericFunction<T>(T t)
{
if (t == null) return default(T);
if (t is Array)
{
Type elementType = t.GetType().GetElementType();
MethodInfo method = ArrayMethod.MakeGenericMethod(new[] elementType);
return (T) method.Invoke(null, new object[] { t });
}
...
}
Note that this will still fail for rectangular arrays, which get even harder to cope with.
It is not possible. T can never be T[]. T is always certain type, not just placeholder. If T is array (int[]) then T[] will be int[][].
Edit: There are some exceptions (like object is object[]), but in general case (and thats what generics are) T can't be T[]