简体   繁体   中英

C# Generic Interface with different return types

I have webservice that can return data in multiple formats. For example json and xml. I'm building a simple C# api against this webservice and I would like the methods to be able to return either fully serialized objects from json, raw json or raw xml. For example:

List<Order> GetOrders(int customerId)
string GetOrders(int customerId)
XMLDocument GetOrders(customerId)

Customer GetCustomer(int customerId)
string GetCustomer(int customerId)
XMLDocument GetCustomer(int customerId)

I have an idea about doing a fluent api where you would call a SetFormat() method which would then return a common Interface for the above methods. But I'm stuck on how that interface would look like, since the implementation that returns serialized objects returns objects of different types.

Another simpler solution would to just have the methods that return serialized objects and then add an out paramater like this:

List<Order> GetOrders(int customerId, out string data)

but that's not very nice solution I think....

UPDATE

I prefered the none-generic solution that Sjoerd suggested, I had over-complicated my problem. This is what I ended up doing:

public class ServiceEntity {
    List<Order> GetOrders(int customerId)....
}    
public class ServiceJson {
    string GetOrders(int customerId)....
}
public class ServiceXml {
    XmlDocument GetOrders(int customerId)....
}

Then a fluent service class like this:

public class Service : IService {
    ....
    public AsJson() { return new ServiceJson(); }
    public AsEntity() { return new ServiceEntity(); }
    public AsXml() { return new ServiceXml(); }
}

Used like this:

string json = Service.New().AsJson().GetCategories(1);
List<Order> = Service.New().AsEntity().GetCategories(1);

Thanks for all the replies!

Just create a generic return class

public class FormattedClass<T>
{
    List<T> ItemList { get; set; }
    string Json { get; set; }
    XmlDocument Document { get; set; }
}

public FormattedClass<T> GetItems<T>(long primaryKey)

You could also have a look at using an implicit operator to return the data as expected.

For example the following class:

public class OrderThingy 
{
    public static implicit operator List<Order>(OrderThingy orderthingy)
    {
        // Use CustomerId to get the data
        return new List<Order>();
    }

    public static implicit operator string(OrderThingy orderthingy)
    {
        // Use CustomerId to get the data
        return "string representation";
    }

    public static implicit operator XDocument(OrderThingy orderthingy)
    {
        // Use CustomerId to get the data
        return new XDocument();
    }

    private int CustomerId { get; set; }

    public OrderThingy GetOrders(int customerId)
    {
        CustomerId = customerId;
        return this;
    }
}

And you could use it like this:

XDocument doc = new OrderThingy().GetOrders(1);
List<Order> orders = new OrderThingy().GetOrders(2);
string stringorders = new OrderThingy().GetOrders(2);

It's nice to try to use generics, but generics are not a silver bullet!

In this case, I wonder how much code would you save compared with the non-generic:

List<Order> GetOrdersAsList(int customerId)
string GetOrdersAsString(int customerId)
XMLDocument GetOrdersAsXml(customerId)

I bet almost none!

And in case you decide on the non-generic approach, most likely it would end up internally as:

List<Order> GetOrders(int customerId)
string OrdersToString(List<Order> orders)
XMLDocument OrdersToXml(List<Order> orders)

Then the latter two methods could be moved to separate classes, resulting in the fact that your GetOrders() is uncoupled from the format.

That seems to me a much better and cleaner approach than trying to use generics in this case!

UPDATE : Don't get me wrong, I like generics and in many cases they make code more readable. Several other answers are very interesting as they show techniques to achieve that. So I recommend to study them: they might be useful in other cases. But in this case , each proposed solution so far has a practical drawback. That's why I recommend non-generics in this case.

What about a method

private class WebService
{
    public T GetOrders<T>(int customerId)
    {
        if (typeof(T) == typeof(List<Order>))
        {
            return new List<Order>();
        }
        if (typeof(T) == typeof(string))
        {
            return "42";
        }
    }
}

and depending on the type you provide on call, like:

var webService = new WebService();

var a = webService.GetOrders<List<Order>>();
var b = webService.GetOrders<string>();

you return the expected value.

仿制药呢?

T GetOrders<T>(int customerID)

Perhaps encapsulate the different return types into a single common type:

CustomerResult GetCustomer(int customerId)

where

CustomerResults has:

Get/SetFormat()
Get/SetCustomer()
Get/SetXmlDoc()
Get/SetString()

etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM