I'm wanting to create a generic lookup method to where you specify a type of object and some other identifying parameters and the method returns an object of that type. Is this possible?
I'm thinking something like this.
public T GetObjectOfType(Guid ID, typeof(T Class))
{
//lookup this object and return it as type safe
}
I know this just won't work but i hope it explains the concept
You can use a generic method for this:
public T GetObjectOfType<T>(Guid id) where T: class, new()
{
if (id == FooGuid) //some known identifier
{
T t= new T(); //create new or look up existing object here
//set some other properties based on id?
return t;
}
return null;
}
If all you want is create an instance of a specific type you do not need the additional id parameter, I assume you want to set some properties etc. based on the id. Also your class must provide a default constructor, hence the new() constraint.
Typically factory methods do not take the type of object to create. They return a type that implements some common interface and the concrete, underlying type is dependent on some argument, typically an enumerated value. A simple example:
interface Whatever
{
void SomeMethod();
}
class A : Whatever { public void Whatever() { } }
class B : Whatever { public void Whatever() { } }
enum WhateverType { TypeA, TypeB }
public void GetWhatever( WhateverType type )
{
switch( type )
{
case WhateverType.TypeA:
return new A();
break;
case WhateverType.TypeB:
return new B();
break;
default:
Debug.Assert( false );
}
}
There you have type safety. I'm not sure how you would implement something like that with generics as you need to supply the generic argument at compile time.
Related
I have a non-generic IList which i would like to cast depending on an enum value during runtime.
I can not change the type of the non-generic IList in the implementation, because it is library code.
Also i do not want to use Reflection (because it is slow) or the dynamic keyword (because it is unsafe and could lead to mistakes down the road).
In the Library Code there is following class:
public class ListView //THIS IS LIBRARY CODE AND CAN NOT BE CHANGED
{
public IList itemsSource { get; set; }
}
Then in my CustomListView class which inherits from ListView i want to cast the itemsSource to the appropiate class depending on ItemType.
Another limitation is that CustomListView can not be generic (dictated by the library we use)
public class CustomListView : ListView
{
public dynamic GetItems()
{
switch (ItemType)
{
case ItemType.A:
return itemsSource.Cast<A>().ToList();
case ItemType.B:
return itemsSource.Cast<B>().ToList();
default:
throw new InvalidOperationException("Not supported");
}
}
}
But i want it to directly return the correct type instead of using dynamic.
Something like (the code below does not work!):
public IList<T> GetItems(ItemType itemType) //The type of T should change depending on what i return
{
switch (ItemType)
{
case ItemType.A:
return itemsSource.Cast<A>().ToList();
case ItemType.B:
return itemsSource.Cast<B>().ToList();
default:
throw new InvalidOperationException("Not supported");
}
}
How would i implement that?
Edit/Appendage
As you guys pointed out i should clarify some things.
Class A and B do indeed have the same base class.
But i want to be able to not cast it from the base type again (because i already cast it in the GetItems() method and also the value of ItemType is known).
I want to be able to do following
IList<A> aItems = listView.GetItems()
Without casting.
The idea behind all of this is to have a general CustomListView which can handle multiple item types. Those items will be added to the itemSource. The type of those items is determined by the ItemType.
I use it like this for example
public class UiForClassA
{
public void Foo()
{
CustomListView customListView = new CustomListView(ItemType.A);
IList<A> itemsOfCustomListView = customListView.GetItems(); //No cast needed because it should somehow implicitly know that.
}
}
I do not want to use Casts everywhere where i use the CustomListView. It somehow should implicitly return the correct items.
And for your information i use the Unity UI Toolkit Framework, but that is not really relevant to the Question.
Think about how this method would be used:
ItemType itemType = ...;
var x = listView.GetItems(itemType);
The value of itemType could be A, or it could be B. So what should the type of x be? It's either IList<A>, or IList<B>, and the only way to express that is to use a type that both IList<A> and IList<B> inherit from, which takes us back to IList. If you don't know the type that you want at compile time, then there is not much point trying to change the return type to something more specific, because you will not be able to access it as the more specific type without reflection.
If you do know the type that you want at compile time, then provide overloads of the method that return the desired type:
public IList<A> GetAItems() { ... }
public IList<B> GetBItems() { ... }
Edit
If you really want to use a generic method, you could do something like this:
public IList<T> GetItems<T>()
{
return itemsSource.Cast<T>().ToList();
}
But I don't see the point in making a method for this, rather than putting that code directly where you need the specific type.
Edit 2
Having seen your use case, I really would recommend you make the CustomListView class generic. I know you say the library prevents that, but there are ways to work around this. You've made an ItemType enum, you could instead make a subclass of CustomListView<T> for each item type:
public class CustomListView<T> : ListView
{
public IList<T> GetItems() => itemsSources.Cast<T>().ToList();
}
public class CustomListViewOfA : CustomListView<A> { }
public class CustomListViewOfB : CustomListView<B> { }
public class UiForClassA
{
public void Foo()
{
var customListView = new CustomListViewOfA();
var itemsOfCustomListView = customListView.GetItems(); //No cast needed
}
}
How do you expect to use such method? The type parameter is set by the caller of the method. Setting type parameter from withing the method is not possible. Please, specify the usage.
You can try these:
IList<T> GetItems<T>(ItemType itemType)
{
switch (itemType)
{
case ItemType.A:
return (IList<T>)(IList)(itemsSource.Cast<A>().ToList());
case ItemType.B:
return (IList<T>)(IList)(itemsSource.Cast<B>().ToList());
default:
throw new InvalidOperationException("Not supported");
}
}
IList GetItems(ItemType itemType)
{
switch (itemType)
{
case ItemType.A:
return itemsSource.Cast<A>().ToList();
case ItemType.B:
return itemsSource.Cast<B>().ToList();
default:
throw new InvalidOperationException("Not supported");
}
}
Or, as suggested by #Qwertyluk in comments, something like this:
private IList<object> GetItems(ItemType itemType)
{
switch (itemType)
{
case ItemType.A:
case ItemType.B:
return itemsSource.Cast<object>().ToList();
default:
throw new InvalidOperationException("Not supported");
}
}
So after implemented Page Object Pattern using this tutorial i have several
Pages that derived from BasePageElementMap.
And i want to handle some operation so i have this class:
public class DownloadAttachmentsHandler
{
public DownloadAttachmentsHandler(BasePageElementMap basePageElementMap)
{
Type type = basePageElementMap.GetType();
}
}
Every Pages that derived from BasePageElementMap have this html elements that locate inside its class that derived from BasePageElementMap and from this Page i have this Map object that contains all my HTML elements that i am using.
public class YahooEmailPage: BasePage<YahooEmailPageElementMap, YahooEmailPageValidator>...
so in case i am call this function like this:
UploadAttachmentsHandler att = new UploadAttachmentsHandler(new YahooEmailPage().Map);
I want to cast this into YahooEmailPage from my DownloadAttachmentsHandler method.
So currently i have this type object, how can i case it into YahooEmailPage ?
If I understood correctly, you want the following:
public class DownloadAttachmentsHandler
{
public static object Cast(object obj, Type t)
{
try
{
var param = Expression.Parameter(obj.GetType());
return Expression.Lambda(Expression.Convert(param, t), param)
.Compile().DynamicInvoke(obj);
}
catch (TargetInvocationException ex)
{
throw ex.InnerException;
}
}
public DownloadAttachmentsHandler(BasePageElementMap basePageElementMap)
{
Type type = basePageElementMap.GetType();
dynamic foo = Cast(basePageElementMap, type);
}
}
Based on this answer by balage.
EDIT: For the example, lets assume that GetType() returns the type bar. You will have to create a method like this one:
public static void UseDynamic(bar input)
{
// Stuff
}
And then do
public DownloadAttachmentsHandler(BasePageElementMap basePageElementMap)
{
Type type = basePageElementMap.GetType();
dynamic foo = Cast(basePageElementMap, type);
UseDynamic(foo);
}
You can use overloads to avoid having to write many ifs or a switch. However, whichever approach you take, you will have to create a method for each possible type.
I have a method that is overloaded 3 times with the exact same functionality, the only change is one parameter which is a list of a concrete type.
private void _doWork(string name, List<TargetItem> members)
{
foreach(var member in members)
{
_doExtraWork(member.TimeToWork);
}
}
private void _doWork(string name, List<NonTargetItem> members)
{
foreach(var member in members)
{
_doExtraWork(member.TimeToWork);
}
}
Thing to know here is the 2 classes in the lists are coming from a WSDL definition. The operation in the iteration is on shared members as they derive from the same base class, however this inheritance is abstracted in the WSDL.
I have tried to do something like this -
private void _doWork<T>(string name, List<T> members)
{
var commonList = new List<>(); /// what type should I use here?
if(typeof (T) == typeof(TargetItem))
{
commonList = members; // assume equal to means copying members to commonList with type conversion
}
if(typeof (T) == typeof(NonTargetItem))
{
commonList = members;
}
foreach(var member in commonList)
{
_doExtraWork(member.TimeToWork);
}
}
Is this the right way to approach the problem and refactor this common code, or am I missing something here?
you can do something like this
private static void _doWork<T>(string name, T members) where T : IEnumerable
{
foreach(var member in members)
{
_doExtraWork(member.TimeToWork);
}
}
In your Calling Code
_doWork("sdfsd", new List<TargetItem>()); // here just as example I am passing new instance
_doWork("sdfsd", new List<NonTargetItem>()); // here just as example I am passing new instance
As the List<T> is of type IEnumerable<T> which in turn is of type IEnumerable. You can add IEnumerable generic constaints in your generic functions. In this way, you do not have to do type checking in your generic functions.
If you want to implement single doExtraWork method then you need to have CommonType for both TargetItem and NonTargetItem. you can solve this using Adapter Pattern like below
Interface IItem
{
int TimeToWorkAdapt {get;}
}
//Now create a wrapper class for TargetItem and NonTargetItem
Class TargetItemAdapt : TargetItem,IItem
{
public int TimeToWorkAdapt
{
get { base.TimeToWork;}
}
}
Class NonTargetItemAdapt : NonTargetItem,IItem
{
public int TimeToWorkAdapt
{
get { base.TimeToWork;}
}
}
// write a generic function which wrap calls to your do extra work method but with generic constriants to interface
private static void _doExtraWork<T>(T members) where T : IItem
{
_doExtraWork(member.TimeToWorkAdapt);
}
// In your Main program...now use our wrapper classes
_doWork("sdfsd", new List<TargetItemAdapt>()); // here just as example I am passing new instance
_doWork("sdfsd", new List<NonTargetItemAdapt>()); // here just as example I am passing new instance
I chose to downcast from the caller
_doWork("Target", Object.TargetItems.ToList<BaseClass>());
_doWork("NonTarget", Object.NonTargetItems.ToList<BaseClass>());
I'd like to use a certain operation for multiple variable types (both native and objects) so I'm using the generic return type as follows.
private Generic Field<Generic>(String field)
{
if (BagOfJunk.Properties.Contains(field))
return (Generic)BagOfJunk[field];
return default(Generic);
}
This works well (and BagOfJunk is just a property of this from which I'm pulling out Object typed values). Now, during run-time, when a field isn't contained in the bag, I get the default value to be null. Hence, in the code, I need to perform a check as follows.
NumericType protoNumber = Field<NumericType>("beep");
int number = protoNumber != null ? protoNumber.Value : -1;
DateType protoDate = Field<DateType>("boop");
DateTime date = protoDate != null ? protoDate.Value : null;
I'd like to make the code more compact, so I tried to design a method that does the above four lines in one swoop, for a generic type. The result is below but, of course, it doesn't compile, because the type GenericIn isn't specific enough to have a property Value.
private GenericOut Field<GenericIn, GenericOut>(String field)
{
GenericIn input = Field<GenericIn>(field);
if (input != null)
return (GenericOut)input.Value;
return default(GenericOut);
}
How can I ensure the computer that my GenericIn isn't general - by promising that whatever stuff I'll shove into it, it'll always have the property Value in it?
Edit
It should be emphasized that the type of Value needs to be generic ( equivalent to GenericOut). I noticed that I didn't stress that strongly enough. So the interface that can be used need to declare a property of general type like the following.
interface ObjectWithValue { public Generic Value { get; } }
You can use an interface and apply a where constraint on the type to implement that interface, like below:
interface IHasPropertyValue<TValue> {
TValue Value { get; }
}
class MyType {
public TValue Method<T, TValue>(T obj) where T : IHasPropertyValue<TValue> {
return obj.Value;
}
}
EDIT: Modified the code above to make it more specific to the comment asked below.
put that property in an interface (or a class) and use the generic constraint "where":
public interface IMyInterface
{
public object Value { get; set; }
}
public class C<T> where T:IMyInterface
To build upon the answers so far, you need to create an interface that will be implemented by your GenericIn that will both guarantee that it has a property Value and that the property is of type GenericOut.
interface IHasValue<TOut>
{
TOut Value { get; }
}
private TOut Field<TIn, TOut>(string field) where TIn : IHasValue<TOut>
{
var input = Field<TIn>(field);
return input == null ? default(TOut) : input.Value;
}
I have this generic singleton that looks like this:
public class Cache<T>
{
private Dictionary<Guid, T> cachedBlocks;
// Constructors and stuff, to mention this is a singleton
public T GetCache(Guid id)
{
if (!cachedBlocks.ContainsKey(id))
cachedBlocks.Add(id, LoadFromSharePoint(id))
return cachedBlocks[id];
}
public T LoadFromSharePoint(Guid id)
{
return new T(id) // Here is the problem.
}
}
The error message is:
Cannot create an instance of type T because it does not have the new() constraint.
I have to mention that I must pass that id parameter, and there is no other way to do so. Any ideas on how to solve this would be highly appreciated.
Normally you would constrain the type T to a type that has a default constructor and call that. Then you'd have to add a method or property to be able to provide the value of id to the instance.
public static T LoadFromSharePoint<T>(Guid id)
where T : new() // <-- Constrain to types with a default constructor
{
T value = new T();
value.ID = id;
return value;
}
Alternatively since you specify that you have to provide the id parameter through the constructor, you can invoke a parameterized constructor using reflection. You must be sure the type defines the constructor you want to invoke. You cannot constrain the generic type T to types that have a particular constructor other than the default constructor. (E.g. where T : new(Guid) does not work.)
For example, I know there is a constructor new List<string>(int capacity) on List<T>, which can be invoked like this:
var type = typeof(List<String>);
object list = Activator.CreateInstance(type, /* capacity */ 20);
Of course, you might want to do some casting (to T) afterwards.
To do this you should specify what T is. Your Cache<T> can hold anything? Tiger, Fridge and int as well? That is not a sound design. You should constrain it. You need an instance of T which will take a Guid to construct the instance. That's not a generic T. Its a very specific T. Change your code to:
public class Cache<T> where T : Cacheable, new()
{
private Dictionary<Guid, T> cachedBlocks;
// Constructors and stuff, to mention this is a singleton
public T GetCache(Guid id)
{
if (!cachedBlocks.ContainsKey(id))
cachedBlocks.Add(id, LoadFromSharePoint(id))
return cachedBlocks[id];
//you're first checking for presence, and then adding to it
//which does the same checking again, and then returns the
//value of key again which will have to see for it again.
//Instead if its ok you can directly return
//return cachedBlocks[id] = LoadFromSharePoint(id);
//if your LoadFromSharePoint is not that expensive.
//mind you this is little different from your original
//approach as to what it does.
}
public T LoadFromSharePoint(Guid id)
{
return new T { Key = id }; // Here is no more problem.
}
}
public interface Cacheable
{
Guid Key { get; set; }
}
Now derive all the cacheables (whatever Ts that you will pass it for Cache<T>) from the interface Cacheable.
In order to use the constructor of a Generic Type without any constraint, and within the class, the syntax where T : class, new() needs to be used
This enables to change values of attributes (fields) - not only get/set properties) at runtime depending the target class used
First, declaring the generic class:
public class Foo<T> where T : class, new()
{
public T oneEmptyElement()
{
return new T();
}
public T setAttribute(string attributeName, string attributeValue)
{
T objT = new T();
System.Reflection.FieldInfo fld = typeof(T).GetField(attributeName);
if (fld != null)
{
fld.SetValue(objT, attributeValue);
}
return objT;
}
public List<T> listOfTwoEmptyElements()
{
List<T> aList = new List<T>();
aList.Add(new T());
aList.Add(new T());
return aList;
}
}
Declare then a potential target class:
public class Book
{
public int name;
}
And finally the call can be done like this:
Foo<Book> fooObj = new Foo<Book>();
Book aBook = fooObj.oneEmptyElement();
aBook.name = "Emma";
Book anotherBook = fooObj.setAttribute("name", "John");
List<Book> aListOfBooks = fooObj.listOfTwoEmptyElements();
aListOfBooks[0].name = "Mike";
aListOfBooks[1].name = "Angelina";
Console.WriteLine(aBook.name); //Output Emma
Console.WriteLine(anotherBook.name); //Output John
Console.WriteLine(aListOfBooks[0].name); // Output Mike
Console.WriteLine(aListOfBooks[1].name); // Output Angelina