How to return a generic object from a method in C#
I have a similar problem to this question only I am not learning generics so want a practical solution and my method is returning more than an int
or a double
.
I have a function that may return an object (from a known set of classes which are unrelated to each other) based on the user input.
For example if I have following two classes
public class A
{
}
And
public class B
{
}
And my method is:
public Object getObject(int objType)
{
//return appropriate object based on objType (1==A, 2==B);
}
I can use statements like if (getObject(int objType) is A)
in my code but that will require a lot of if
statements which is not a good solu开发者_运维技巧tion (IMHO).
So I am looking for a better design solution to solve this problem. Any suggestions please?
EDIT
I will need to use the returned object for some time and might also need to pass it to some other method or return a reference of it. So expecting some moving around of that object. Please keep that in mind too while suggesting some approach to solve this problem.
Constructing a different type of object based on some input sound like you could use the Factory design pattern.
EDIT: If you want callers to not check for the object type then the Factory pattern is becoming a viable option as it leverages polimorphism. You return the actual object into a variable of base class type and when you call a method the right code will be runned depeding on the actual object type.
If your types are unrelated then it is worth while to see if you can make them share a base class because uing polimorphism is more elgant way to solve the problem than a big switch.
What about a switch/case block?
public Object getObject(int objType)
{
switch( objType )
{
case 1: return new A();
case 2: return new B();
}
}
Note that this has nothing to do with generics.
EDIT
After reading the comments, I see that this is not what you need after all. According to the new information, I see only two ways to get (somewhat) what you need.
- If possible, make a base class that both
A
andB
inherit from (Lets call itC
in this case), and make yourgetObject
method return a object of C instead. - Make the method generic
public T getObject<T>()
. This means that the caller must send the object type instead of 1, or 2 into thegetObject
method, but then at least he knows what he will get in return.
As long as the method returns object
, and just takes a parameter of int
that decides which type of object to create, there is no way you can find out what kind of object you get in return short of checking the object itself, and if it's many types of objects that can be returned, that kind of code will very quickly get messy.
If i have a class that has to return an object and i have to get it from a special type i normally have somewhere a set of factory functions in a dict like Dictionary<Type, Func<Type>>
(maybe with parameters like Func<P1, P2, ..., Type>
. Then i write those functions and put them into the dictionary. Then you can easily get the correct object out of it.
Another approach (if you have access to the source code of the objects and they have something to share) you could add an Interface to them and then create a List<MyInterface>
. If your objects are in the correct order (if you like to use your int objType
) you could simply get the right one by calling myList[index]
.
If you just want to get new instance of the provider class, you can use this function.
public T GetObject<T>() where T : new()
{
return new T();
}
No need for mentioning objectType
FYI, you're implementing a type of Factory pattern.
In general, there are only two ways to avoid a switch, when dealing with a function that varies behavior on type:
- Take advantage of polymorphism
- Use a dictionary mapping from the source value to the target type (which is nearly the same as a switch - same amount of code, same maintainability problems)
In your example, input to the factory is a completely unrelated type, so you can't use polymorphism.
If you could require the user to pass in some custom type, then you could take advantage of polymorphism in your factory:
public class Selection
{
public abstract object Select();
}
public class SelectionA : Selection
{
public override object Select()
{
return new A();
}
}
public class SelectionB : Selection
{
public override object Select()
{
return new B();
}
}
// ...
public Object getObject(Selection selection)
{
return selection.Select();
}
If A
and B
are anything like each other, then you really should make a common base class for them, or if you can't touch that code, use the Adapter pattern to make 3 more classes, that do have the correct hierarchy.
If they aren't anything like each other, and you must have int
as the input to your factory method, then a switch, if/else-if chain, or dictionary is your only option:
public Object getObject(int objType)
{
switch(objType)
{
// etc
}
}
Or
public Object getObject(int objType)
{
if(objType == 1)
return new A();
// etc
}
Or
public Object getObject(int objType)
{
var mapping = new Dictionary<int, Type>()
{
{ 1, typeof(A) },
{ 2, typeof(B) },
};
// use reflections to instantiate based on type
if(!mapping.ContainsKey(objType))
throw new ArgumentException("Invalid type ID specified", "objType");
System.Activator.CreateInstance(mapping[objType]);
}
What is bad in your approach - client code anyway should know what type of object was returned by this method (there is no polymorphism, so you can't return object of base class type):
A a = (A)getObject(1)
B b = (B)getObject(2)
BTW its better to use Enum for defining type of object, rather than int.
So, as client knows what method will return, why don't you create two separate methods for client to use?
A a = getA()
B b = getB()
If those objects have same purpose, it's better to use inheritance form base class here:
C c = getObject(ObjectType.A);
I would use Adeel's answer in combination with a Dictionary to store the combination of Types with ints. Since you use ints to identify the type you need a form of mapping between them two anyway.
Dictionary<int, Type> dictionary = new Dictionary<int, Type>();
//fill the Dictionary with all int/Type-combinations
{
int objectType;
Type type = dictionary[objectType];
var genericMethodInfo = this.GetType().GetMethod("GetObject").MakeGenericMethod(type);
object newObject = genericMethodInfo.Invoke(this, null);
var objectInCorrectType = Convert.ChangeType(newObject, type);
}
public T GetObject<T>() where T : new()
{
return new T();
}
Well you really have to use if statements. I might do something like:
var result = getObject(...);
if (result is A) { ... }
else if (result is B) { ... }
However, you have a very strange method called getObejct(). Since, you must pass it an int to get the resulting type; that implies that you already know the type of object you want. So, when you want an A do "var a = (A) getObject(1)" and when you want a B do " var b = (b) getObject(2)".
精彩评论