Like this:
var result1 = GetData<double>(someString);
var result2 = GetData<string>(someString);
What is this meant?
< >
How to return data?
public object GetData<Type>(string a)
{
// How do I know I should return this...
return 2.2;
// or this?
return "2.2";
}
What you're seeing here is called Generics. You can read about them in detail at the MSDN. In short, you can specify a Type Parameter along with your method. Based on the type, different code paths can be adopted. Read the link for examples and such.
In your particular case, the return type is independent of the type parameter. You are dealing with two different things. So, while returning, you can use any of those two statements.
return 2.2;
This will return a long 2.2. While the other statement will return a string which is "2.2". Since your return type is Object, both are acceptable. Though you haven't said that in problem, what you're looking for is something like this:
//Your generic GetData method
public TEntity GetData<TEntity>(string arg1)
{
var toReturn;
//Do some calculations
return (TEntity)toReturn;
}
Now, when you make a call like this:
var t = GetData<string>(string1);
variable t will hold a string. If you do a call like this:
var t = GetData<int>(string1);
variable t will hold and int.
This is a type parameter for generic method.
The term you're looking for is Generics. These are type arguments into the method, which mean that the method/class can work with specific types in a type-safe manner rather than falling back to everything being object (which also has a performance impact with boxing/unboxing).
From the C# guide:
Generics allow you to define type-safe data structures, without
committing to actual data types. This results in a significant
performance boost and higher quality code, because you get to reuse
data processing algorithms without duplicating type-specific code.
A few quick links:
.NET Generics in a Nutshell
An Introduction to C# Generics
That symbol after a method called Generic Method. It will defer the type inference during runtime.
Your GetData may be rewritten as below provided that you control the calling method as
GetData<float>("2.2")
GetData<string>("2.2")
public static T GetData<T>(string a)
{
return (T)Convert.ChangeType(a, typeof(T));
}
Related
I have a static function:
public static MyCollection<T> Parse<T>(string? json) where T : BaseClass
{
...
}
And it works fine. It makes decisions internally based on the type and produces the correct collection.
But, the collection I want is based on a file name coming in from another source and I was hoping to have one collection and dynamically set the type, as in:
Type t;
if (name == "Certain name") t = typeof(CertainChildClassOfBaseClass);
if (name == "Other name") t = typeof(OtherChildClassOfBaseClass);
var myCollection = ParsingService.Parse<t>(jsonString);
This obviously doesn't work, but is what I'm trying possible? I don't really want to write a bunch of if/else blocks with nearly identical content just so each one can have a different type (I already wrote those type-specific blocks inside the Parse function - doing the same branching twice seems like maybe I've made a more fundamental mistake). I am open to suggestions on how to do this the "right" way. Maybe I have to do this with reflection, which doesn't thrill me, but isn't the end of the world. One possible issue is the my parsing service is static.
Help is always appreciated.
Usually, I would recommend making Parse non generic, and then have a generic overload:
public static MyCollection<BaseClass> Parse(Type type, string? json)
{
...
}
public static MyCollection<T> Parse<T>(string? json) where T : BaseClass
{
return (MyCollection<T>)Parse(typeof(T), json);
}
But you can't make MyCollection contravariant because it's a concrete class, so that cast is illegal. And anyway, contravariant containers are dangerous to use (just look at arrays, for example).
The other method is to use reflection to get the version of the method you want (a process known as "closing" the generic type):
var genericMethod = typeof(/* class containing Parse */).GetMethod("Parse");
var method = genericMethod.MakeGenericMethod(type); // type will be substituted for your 'T', above
object collection = method.Invoke(json); // call the method
And here we hit another snag: collection's type will be object, and you need to cast it to something else. But you can't use MyCollection<T> because you don't know T, and you can't use MyCollection<BaseClass> because, well, no contravariance, again.
So really the main blocker is that MyCollection type. As long as it is that way, you have no easy way to go around it. You could instead return a non generic collection, like ICollection or IList, but then you lose type safety and you'll have to keep casting more later. It's up to you, really.
I want to do all of the same code twice, but just for two different objects. I am getting these objects from a SOAP API (rather outdated one).
I want to avoid writing duplicate code over and over again. I'll be taking my original object and manipulating it a lot (all the same way) the only difference is that the object type is the difference, so that's why I need to make a generic object. I want it to return the same type as it takes as a parameter. I am having an issue that if I do code like this
public static class ParamsHelper<T>
{
public static async Task<T[]> Whatever(T[] rptParams)
{
//do some stuff to rptparams
return rptParams;
}
}
// then I call it like this below:
var params = await ParamsHelper.Whatever<ItemP[]>(new ItemP[]{});
// it says can't convert type ItemP[] to type ItemP[][].
Additionally, I am using LINQ to do all of the manipulating. I would love advice on how to access the object fields (maybe reflection?)
You have to declare the generic type on the method.
public static async Task<T[]> Whatever<T>(T[] rptParams)
The usage generally is implied from usage so you don't have to pass it.
if you do have to pass it, dont make it an array.
ParamsHelper.Whatever<ItemP>(new ItemP[] { });
This must be a duplicate but i haven't found it. I've found this question which is related since it answers why it's recommended to use a method group instead of a lambda.
But how do i use an existing method group instead of a lambda if the method is not in the current class and the method is not static?
Say i have a list of ints which i want to convert to strings, i can use List.ConvertAll, but i need to pass a Converter<int, string> to it:
List<int> ints = new List<int> { 1 };
List<string> strings = ints.ConvertAll<string>(i => i.ToString());
This works, but it creates an unnecessary anonymous method with the lambda. So if Int32.ToString would be static and would take an int i could write:
List<string> strings = ints.ConvertAll<string>(Int32.ToString);
But that doesn't compile - of course. So how can i use a method group anyway?
If i'd create an instance method like this
string FooInt(int foo)
{
return foo.ToString();
}
i could use strings = ints.ConvertAll<string>(FooInt);, but that is not what i want. I don't want to create a new method just to be able to use an existing.
There is an static method in the framework, that can be used to convert any integrated data type into a string, namely Convert.ToString:
List<int> ints = new List<int> { 1 };
List<string> strings = ints.ConvertAll<string>(Convert.ToString);
Since the signature of Convert.ToString is also known, you can even eliminate the explicit target type parameter:
var strings = ints.ConvertAll(Convert.ToString);
This works. However, I'd also prefer the lambda-expression, even if ReSharper tells you something different. ReSharper sometimes optimizes too much imho. It prevents developers from thinking about their code, especially in the aspect of readability.
Update
Based on Tim's comment, I will try to explain the difference between lambda and static method group calls in this particular case. Therefor, I first took a look into the mscorlib disassembly to figure out, how int-to-string conversion exactly works. The Int32.ToString method calls an external method within the Number-class of the System namespace:
[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), SecuritySafeCritical]
public string ToString(IFormatProvider provider)
{
return Number.FormatInt32(this, null, NumberFormatInfo.GetInstance(provider));
}
The static Convert.ToString member does nothing else than calling ToString on the parameter:
[__DynamicallyInvokable]
public static string ToString(int value)
{
return value.ToString(CultureInfo.CurrentCulture);
}
Technically there would be no difference, if you'd write your own static member or extension, like you did in your question. So what's the difference between those two lines?
ints.ConvertAll<string>(i => i.ToString());
ints.ConvertAll(Convert.ToString);
Also - technically - there is no difference. The first example create's an anonymous method, that returns a string and accepts an integer. Using the integer's instance, it calls it's member ToString. The second one does the same, with the exception that the method is not anonymous, but an integrated member of the framework.
The only difference is that the second line is shorter and saves the compiler a few operations.
But why can't you call the non-static ToString directly?
Let's take a look into the ConvertAll-method of List:
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
{
if (converter == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter);
}
List<TOutput> list = new List<TOutput>(this._size);
for (int i = 0; i < this._size; i++)
{
list._items[i] = converter(this._items[i]);
}
list._size = this._size;
return list;
}
The list iteraterates over each item, calls the converter with the item as an argument and copys the result into a new list which it returns in the end.
So the only relation here is your converter that get's called explicitly. If you could pass Int32.ToString to the method, the compiler would have to decide to call this._items[i].ToString() within the loop. In this specific case it would work, but that's "too much intelligence" for the compiler. The type system does not support such code conversions. Instead the converter is an object, describing a method that can be called from the scope of the callee. Either this is an existing static method, like Convert.ToString, or an anonymous expression, like your lambda.
What causes the differences in your benchmark results?
That's hard to guess. I can imagine two factors:
Evaluating lambdas may result in runtime-overhead.
Framework calls may be optimized.
The last point especially means, that the JITer is able to inline the call which results in a better performance. However, those are just assumptions of mine. If anyone could clarify this, I'd appreciate it! :)
You hit the nail on the head yourself:
This works, but it creates an unnecessary anonymous method with the
lambda.
You can't do what you're asking for because there is no appropriate method group that you can use so the anonymous method is necessary. It works in that other case because the implicit range variable is passed to the delegate created by the method group. In your case, you need the method to be called on the range variable. It's a completely different scenario.
casting a var type objects to array of a class.
in my example I can query from the table all elements. but the problem is when i cast it, it just doesnt cast all instances.
any help??
There's no such type as "a var type". A declaration using var just makes the compiler infer the type of the variable. It's still statically typed, and works as if you'd explicitly declared it - although it allows you to declare variables which use an anonymous type.
In your case we don't know what any of the methods involved do, which means we can't really tell what's going on. It sounds like Query is probably of type IEnumerable<AccessPointItem>. You'll need to express in code how to convert from an AccessPointItem to an AccessPoint.
A few points to note:
Your query expression is somewhat pointless - you probably just want to call tsvc.CreateQuery<AccessPointItem>()
Conventionally, local variables in C# use camel casing (starting with lower case letters) not Pascal case
You create an array for no purpose - why?
Select() will never return null, so you don't need to check for it
Calling Cast will attempt to just cast each AccessPointItem to AccessPoint... is that really what you intended?
It looks as though you're you're mixing up your classes AccessPoint and AccessPointItem. Try this:
public static AccessPoint[] getAllAps()
{
return tsvc.CreateQuery<AccessPoint>("AccessPointTable").ToArray();
}
or this:
public static AccessPointItem[] getAllAps()
{
return tsvc.CreateQuery<AccessPointItem>("AccessPointTable").ToArray();
}
There is something wrong with the types involved.
First you read AccessPointItem:
var Query = from APs in tsvc.CreateQuery<AccessPointItem>(...)
Then you try to cast it to AccessPoint:
return Query.Cast<AccessPoint>()
You're gonna need to implement some kind of conversion method in order for that to work (unfortunately I never did it myself, but from a quick Google run I see plenty of info about the subject is available). I'll give it a shot off the top of my head:
//inside AccessPointItem.cs
public static AccessPoint ToAccessPoint(this AccessPointItem item) // extension method looks good for the job
{
AccessPoint retObj = new AccessPoint();
// assign properties from 'item' to 'retObj'
return retObj;
}
//Usage example in your case
return Query.Select(singleAccessPointItem => singleAccessPointItem.ToAccessPoint());
I have this common code:
private bool DoItStartup(IReader reader, Type provider)
{
/// lots of common boiler plate code
/// like:
var abcProvider = reader.ReaderData as AbcProvider;
var xyzProvider = abcProvider.Provisions.FirstOrDefault<XyzProvider>(); // line 2
}
The above lines of code are there for like 50 or some providers, now Line 2 I want to basically do this:
var xyzProvider = abcProvider.Provisions.FirstOrDefault<typeOf(provider)>();
This doesn't work, possibly because xyzProvider doesn't know it's type # compile time? Not sure. But is there a similar pattern I can use. Otherwise I'm having to duplicate this cruft code 50 times :(
Without knowing the type of abcProvider.Provisions it's a bit hard to say for sure... but normally I don't provide any type arguments to FirstOrDefault... I just let type inference work.
Have you tried just calling:
var xyzProvider = abcProvider.Provisions.FirstOrDefault();
?
(The reason it's not working is that type arguments have to be names of types or type parameters; they can't be expressions computed at execution time.)
It sounds like you need to provide a generic parameter to either the method or the class that encloses the code above. The following should work properly (not sure how provider is being passed in
public T GetStuff() {
var xyzProvider = abcProvider.Provisions.FirstOrDefault<T>();
}
You might need to use Generics.
There are several articles on the MSDN that cover this:
Generics (C# Programming Guide)
An Introduction to C# Generics
Generic Methods (C# Programming Guide)
Generic parameters of generic types are determined at compile type (not run time). But you want your code to get type of FirstOrDefault at runtime which causes the error.
Try using this instead:
private bool DoItStartup<T>(IReader reader, Type provider)
{
...
var x=list.FirstOrDefault<T>();
}