C# Generics - Accepting different types - c#

I am trying to write a generic method so I can avoid code duplication. The generic method has to be able to accept one of three different grid view types however I cannot get the following cast to work at the start of the generic method;
var grid;
if (typeof(T) == typeof(GridView))
{
grid = (GridView)gridView;
}
else if (typeof(T) != typeof(BandedGridView))
{
grid = (BandedGridView)gridView;
}
else if (typeof(T) != typeof(AdvBandedGridView))
{
grid = (AdvBandedGridView)gridView;
}
else return;
How can I cast "grid" to either of the three types so I can then do something with them. I am still trying to grasp the idea and concept behind Generics.

If BrandedGridView and AdvBrandedGridView both inherit from GridView you can add a constraint to your generic
...<T> where T : GridView
If not you can use Convert.ChangeType:
Try Convert.ChangeType:
if (typeof(T) == typeof(GridView))
{
var grid = (GridView)Convert.ChangeType(gridView, typeof(GridView));
}
elseif (typeof(T) == typeof(BrandedGridView))
{
var grid = (BrandedGridView)Convert.ChangeType(gridView, typeof(BrandedGridView));
}

You want to constrain type T to something (likely GridView as 2 other types are likely derive from it) so C# knows what method the T has, otherwise it is just of type Object.
public class MyClass<T> where T : GridView
Please read article about generic on MSDN to get more details - http://msdn.microsoft.com/en-us/library/ms379564(VS.80).aspx#csharp_generics_topic4
Note: As mentioned above C# is not JavaScript and "var" does not mean "a type" it is just shorter way to declare object of type of the right side. I.e. in var my = new List() var is synonim for List.

"methods are identical except for the parameter type"
I think you should just make a new method that has the different parameter type of the view as the actual parameters. They're the ones that are different after all.

Related

Can't dynamically cast a collection of objects to a type known at runtime

I know this question was here multiple times but i did some searching and i couldn't find solution that would fit:
private void UpdateCollections<T>(ICollection<T> updatedFormulas,
ICollection<T> currentFormulas) where T: IIdentifiable
{
// Irrelevant code omitted
foreach (var prop in newOrUpdatedFormula.GetType().GetProperties())
{
if (prop.PropertyType != typeof(string) &&
prop.PropertyType.GetInterface(typeof(IEnumerable).Name) != null &&
prop.GetValue(newOrUpdatedFormula) != null)
{
// the dynamic type i need is stored here =>
// prop.PropertyType.GenericTypeArguments[0]
UpdateCollections((ICollection<??>prop.GetValue(newOrUpdatedFormula),
(ICollection<??>prop.GetValue(existingFormula));
}
}
}
I need to go through two collections of objects to compare them. inside of those two collections there are other sub-collections of different type (but still implementing IIdentifiable interface). It goes this way few levels deep. So the '??' type i know at runtime and i know it implements IIdentifiable.
I've tried casting to ICollection<IIdentifiable> and it didn't work, i've tried to play with MakeGenericType() method but also it didn't help.
Something like below won't even compile:
Type typeInCollection = typeof(ICollection<>)
.MakeGenericType(prop.PropertyType.GenericTypeArguments[0])
UpdateCollections(Convert.ChangeType(prop.GetValue(newOrUpdatedFormula),
typeInCollection), Convert.ChangeType(prop.GetValue(existingFormula), typeInCollection));
What's the easiest way to make this recursive method work when i know the type inside ICollection at runtime?

How do I build List or Array Types from a stored runtime Type for the sake of comparing them?

NOTE: When I originally asked this question I included IEnumerable. After some responses, I realized it was obvuscating the question because I was mixing interfaces and classes. I've removed it. Now I'm only asking about these three class types:
a type that represents a single thing
a type that represents a list of those things
a type that represents an array of those things
Before responding, please make absolutely sure you understand what I'm asking. I am NOT asking about:
types that derive from another type
types that are assignable from another type
types that implement some interface
Edited question:
If I have retrieved a type using typeof(), can I somehow use that to check for types that represent lists or arrays of the retrieved type?
I want to do this specifically so that I can do comparisons to find out if an unknown type is either some other specific type or a list or array of that specific type.
Here's a code example. Explanation is below the code.
public void Example()
{
Type k = typeof(Kitten);
Type m = typeof(Whatever); //Could even be a List<Whatever> or Whatever[]
Compare(k, m);
}
public void Compare(Type someType, Type otherType)
{
Type ListOfSomeType = ?????;
Type ArrayOfSomeType = ??????;
if(otherType == someType)
//otherType is a Kitten
elseif(otherType == ListOfSomeType)
//otherType is a List<Kitten>
elseif(otherType == ArrayOfSomeType)
//otherType is a Kitten[]
}
The point is that the Compare function gets Type arguments containing the types. It has no idea what they actually are. But it needs to be able to find out if they are the same type, or if the second one is a TYPE OF list or array storing objects of the first kind of type.
I entered question marks (?????) for the imaginary code I would like to use to construct the collection types.
How can I do this? This question is not hypothetical.
Sure you can do this with reflection. It's quite straightforward.
You can get the open type for List<> and then use MakeGenericType for the list. And Type.MakeArrayType for the array.
public static void Compare(Type someType, Type otherType)
{
var listOfSomeTypeType = typeof(List<>).MakeGenericType(someType);
var arrayOfSomeTypeType = someType.MakeArrayType();
Console.WriteLine("SomeType: {0}",someType.Name);
Console.WriteLine("OtherType: {0}",otherType.Name);
if(someType == otherType)
Console.WriteLine("someType and otherType are the same");
else if(listOfSomeTypeType == otherType)
Console.WriteLine("otherType is a list of someType");
else if(arrayOfSomeTypeType == otherType)
Console.WriteLine("otherType is an array of someType");
else
Console.WriteLine("No match found");
}
Live example: https://rextester.com/HQH48424
Type arrayOfSomeType = someType.MakeArrayType();
Type listOfSomeType = typeof(List<>).MakeGenericType(new[] { someType });

Return a List<type> with a dynamically selected type while also casting to that type (C#)

Resolved, see solution at the end of the post.
I have a method that returns a List of attachments. I have three types of attachments, which all extend a class called GenericAttachment:
GenericAttachment
||
==> FormA_Attachment
==> FormB_Attachment
==> FormC_Attachment
I also have different form types, which all extend a class called GenericForm:
GenericForm
||
==> FormA
==> FormB
==> FormC
The method must take a Type argument which is either FormA, FormB or FormC, and return the attachments of the appropriate type.
I've tried this first:
public static List<GenericAttachment> GetAllAttachmentsByFormID<T>(int sqlFormId, Type type) where T : GenericForm
{
//this returns e.g. FormA_Attachment based on FormA as the input
Type attachmentType = GetAttachmentTypeByFormType(type);
//Call a generic function (overload of this one)
//that returns all attachments and requires a specific type argument.
//Meanwhile, .Invoke()'s return type is just an `object`
var attachments = typeof(AttachmentManager)
.GetMethod("GetAllAttachmentsByFormID", new[] { typeof(int) }) // select the correct overload for the method
.MakeGenericMethod(attachmentType)
.Invoke(new AttachmentManager(), new object[] { sqlFormId });
return (List<GenericAttachment>)attachments;
}
However, the cast fails at runtime ("failed to cast").
Then I tried a dumber way with if/else statements, but it doesn't compile because "Cannot convert List<FormA_Attachment> to List<GenericAttachment>". Tried both using Convert.ChangeType and a normal cast, as seen below.
It's weird that it doesn't compile because e.g. FormA_Attachment extends GenericAttachment.
if (attachmentType == typeof(FormA_Attachment))
{
return (List<FormA_Attachment>) Convert.ChangeType(attachments, typeof(List<FormA_Attachment>));
}
else if (attachmentType == typeof(FormB_Attachment))
{
return (List<FormB_Attachment>)attachments;
}
else if (attachmentType == typeof(FormC_Attachment))
{
return (List<FormC_Attachment>)attachments;
}
else
{
throw new Exception("Invalid attachment class type.");
}
How do I cast the attachments into a List<type>, where type is selected dynamically?
Solution:
Thanks to #Mikhail Neofitov, the below code worked.
attachments has the type object because this is what .Invoke() returns.
So I cast it to a specific type first, then convert to less specific type using .OfType<GenericAttachment>().ToList().
if (attachmentType == typeof(FormA_Attachment))
{
return ((List<FormA_Attachment>) Convert.ChangeType(attachments, typeof(List<FormA_Attachment>))).OfType<GenericAttachment>().ToList();
}
else if (attachmentType == typeof(FormB_Attachment))
...
//similar code
C# does not allow types covariance, it means that List<string> could not simple convert to List<object>.
In your case you could use LINQ extension method OfType() as following:
return attachments.OfType<GenericAttachment>().ToList();
Just a note, I guess, you could modify your app architecture to pass resulting type of GenericArgument to the generic parameter of GenericForm and define an abstract method for returning resulting attachments in the result type. Also, your generic parameter <T> is useless, you do not use it in the method body.
You can utilize covariance here. List<T> isn't variant, but it implements IEnumerable<out T> which is covariant in T. This means you cannot convert your object to the List<GenericAttachment>, but you can convert it to an IEnumerable<GenericAttachment>. So instead of all those ifs, you should be able to do the following:
return ((IEnumerable<GenericAttachment>)attachments).ToList();
Take a look at this. This is an example of memory efficient casting a List<Derived> to a List<Base>

How to return Anonymous Type while using Linq [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
LINQ to SQL: Return anonymous type?
Do any one know how to return an anonymous type. I am using Linq where i need to return the following code
private <What's the return type to be provided here> SampleLinq(Int32 Num)
{
var query = (from dept in obj.DeptTable where dept.Id == Num select new { dept.DeptName, dept.DeptId });
return (query)
}
Sorry to say but you cannot return anonymous type out side the scope of method.
This is the alternate way to get anonmous type
// Method that returns anonymous type as object
object ReturnAnonymous()
{
return new { City="Prague", Name="Tomas" };
}
// Application entry-point
void Main()
{
// Get instance of anonymous type with 'City' and 'Name' properties
object o = ReturnAnonymous();
// This call to 'Cast' method converts first parameter (object) to the
// same type as the type of second parameter - which is in this case
// anonymous type with 'City' and 'Name' properties
var typed = Cast(o, new { City="", Name="" });
Console.WriteLine("Name={0}, City={1}", typed.Name, typed.City);
}
// Cast method - thanks to type inference when calling methods it
// is possible to cast object to type without knowing the type name
T Cast<T>(object obj, T type)
{
return (T)obj;
}
you can use it only for types in one assembly (two anonymous types from two different assemblies will be internally compiled to two different types that can't be converted using this trick).
Return Dynamic type:
public static dynamic getCustomer()
{
.....
var x = from c in customers
select new {Fname = c.FirstName};
return x;
}
static void Main(string[] args)
{
dynamic x = getCustomer();
Console.WriteLine(Enumerable.First(x).Fname);
Console.ReadKey();
}
you can't do that. that is why it is called anonymous. It doesn't have a name. But you always can cast it to object
Well, you can't actually do that, but here's a hack on this.
private object SampleLinq(Int32 Num)
{
return (from dept in obj.DeptTable where dept.Id == Num select new { dept.DeptName, dept.DeptId });
}
You can't return an Anonymous Type from a method.
You can create a simple Class to wrap the Anonymous Type, but you still need a Class (or cast to object).
Keep in mind, though, that if you cast to object there's no way to cast back. You'll need reflection to read any data.
The answers you see from the hack is a lot of work just to get an anonymous type through a method boundary. You shouldn't be doing this. If you need to pass something back from a method, you should be passing concrete types.
It depends what you looking to do with the return vale.
If your going to bind it in the UI
you can just rerun
IEnumerable or IQueryable.
If your going to use reflection on the return value just return type object
If your using c# 4.0 you can return a
dynamic type
If your using EF or Linq to SQL to further join a query comprised of your anonymous type you
can make a concrete class instead and
use the concrete placeholder
technique. For more details on this
technique I can give some assistance.
As others have mentioned though, you should really question whether returning an anonymous type form a method is the best way to solve the problem at hand. In general there is usually a better more pattern based approach that may require a bit more coding up front but may resulting in a more elegant design. This being said, I do believe there are legitimate cases such as with data binding where returning anonymous type instances can be perfectly acceptable.
UPDATE:
Just an interested tidbit I wanted to share in case those reading are not aware. Anonymous types are unique per their property names and types so lets say you have method A and method B in in both you create an anonymous type that has a single string typed property called Name by doing something like be code below.
public object A()
{
return new { Name = "Cid" }
}
public object B()
{
return new { Name = "Galuf" }
}
public void Test()
{
System.Diagnostics.Trace.Assert(A().GetType() == B().GetType());
}
Now even though this type is defined in two separate places the compiler only creates only creates a single shared type because they both have the same set of properties as defined by the property types and property names. In this respect the properties can be thought of as sort of a signature for the anonymous type. Using this knowledge there are different techniques that can be used for introspection into anonymous type instances that have been cast to object or deferred using the dynamic keyword. There are also nifty ways to work with anonymous types by using generic methods just as Linq does with methods in the static Enumerable and Queryable classes. This way you can do things like create a new instance of any given anonymous type and without using reflection. The trick is though that you have to use an instance of the anonymous type to pass to methods in order to have the type be inferred by the generic method. If anybody is interested in these topics further as they apply to the original posters question, leave a comment and I can try to clarify some of these techniques.

How to determine the type of the returned value within the generic method

We've created a generic method like so:
public TReturnType GetValue(string key)
{
var configurationParameter = (from cp in _repository.ConfigurationParameters
where cp.ConfigurationParameterKey == key
select cp).FirstOrDefault();
var returnValue (TReturnType)Convert.ChangeType(configurationParameter.ConfigurationParameterValue, typeof (TReturnType));
return returnValue;
}
Now, we would like to put some error handling in this method so that in case we're expecting a numeric type we can do, for example, an int.TryParse(returnValue, out myNewInt). Of course to be able to do that we would have to be able to determine the type of TReturnType within the method.
Is there a way to do this?
Thanks for all of your help.
Regards.
Sure, but you should consider whether or not this is a good idea. You can always say
if (typeof(T) == typeof(int)) whatever
But doing so is a bit of a bad code smell. The whole point of generics is to be generic. If you have special-purpose code for when T is an integer, then why not simply add another method that handles exactly the integer case?
Sure, just add in some code like this:
if(typeof(TReturnType) == typeof(int))
{
var number = (int)returnValue;
//Validate your output
}

Categories

Resources