Link to home
Start Free TrialLog in
Avatar of purplesoup
purplesoupFlag for United Kingdom of Great Britain and Northern Ireland

asked on

How does a method that returns a generic type return an IEnumerable object?

Here is an example of my problem. If I try to call the generic method with an IEnumerable I get the error Object must implement IConvertible.

Any thoughts on how I can get the method to return an IEnumerable object?

using System;
using System.Collections.Generic;
using System.Linq;

class MainClass {
  public static void Main (string[] args) {
    var r1 = MyGenericMethod<string>("hello");
    Console.WriteLine ($"Hello World: {r1}");

    var r2 = MyGenericMethod<IEnumerable<string>>("hello");
    var r3 = r2.First();
    Console.WriteLine ($"Hello World: {r3}");
  }

  public static TR MyGenericMethod<TR>(string m) {

    if (typeof(TR) == typeof(string)) {
      var result = m;
      return (TR)Convert.ChangeType(result, typeof(TR));
    }

    if (typeof(TR) == typeof(IEnumerable<string>)) {
      var result1 = new List<string>();
      result1.Add(m);
      return (TR)Convert.ChangeType(result1, typeof(TR));
    }

  return default(TR);
  }
}




Open in new window

ASKER CERTIFIED SOLUTION
Avatar of louisfr
louisfr

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Just a comment:

What does your method do? Exactly it converts a value. Thus using a generic method makes no sense, especially as it does not use the generic type.
The far better signature is:

MyConvert(Type destinationType, string value);

Open in new window


And for your actual error: What are you trying to solve here?
Avatar of purplesoup

ASKER

ste5an - sorry I'm not clear about your suggested solution.

The actual code I am working with makes a REST call and passes in the type that it expects back from the call. In certain situations the type has a flag to say if the call worked and if not what the error was. In these cases if the error was in the REST call itself (Unauthorized or Forbidden etc) I wanted to be able to add the error information to these objects rather than just returning a null value.
The actual code I am working with makes a REST call and passes in the type that it expects back from the call.
I don't understand this requirement. Seriously, I don't.

But this is an architectural question (thus you may ignore it):
The call to a method defines the types as its declaration defines its signature which contains them.
Why do you need "dynamic" results?
The only common case in this area I'm aware of is using AutoMapper to assign values to instances automatically. But on a value-type, atomic value?


For your code problem:
On code level using the generic method to carry an parameter is possible, but not correct. Cause in your sample the generic type is nothing else. And on code level, I don't see the advantage of not using the normal, explicit casting/converting when needed.
That's what Louis meant by "Your "generic method" does nothing generic." And because of this, you should not use it.
Thanks ste5an - I would like to understand if there is a better way to do this. Let me share the basic idea here and see what your thoughts are.

So I have a basic service caller:

public TR CallRest<TR>(...) {
}

Inside this is an HttpClient called client

It makes the REST call and returns the result

response = client.GetAsync(method).Result;
if (response != null && response.IsSuccessStatusCode)
           return response.Content.ReadAsAsync<TR>().Result;

So to make a call, I would say something like

var myOrgDetails = CallRest<OrgDetails>(parameters...)

What do you think would be a better way to implement this functionality?
What do you think would be a better way to implement this functionality?
Like this:

OrgDetails myOrgDetails = RestRetrieveOrgDetails(parameters...);

Open in new window

Cause with few given details, it looks right now like you're adding an level of indirection for no reason.

So I have a basic service caller:
public TR CallRest<TR>(string someParameter) { } 

Open in new window

This makes in many scenarios not really sense. Cause I expect that the result of calling this with a constant parameter having the same semantic meaning.

Your above method returning default(T) in some cases does not fulfil this. Cause sometimes it returns a converted value, sometimes not.

Back to your basic REST caller, there is only one basic call method, which makes sense. That one, which returns a string. Cause HTTP does only transport text.
Thanks for your comments.

So that isn't the only call - there would be for example all these:

var myOrgDetails = CallRest<OrgDetails>(parameters...)
var myOrderDetails = CallRest<OrderDetails>(parameters...)
var myItemDetails = CallRest<ItemDetails>(parameters...)
var mySubscriptionDetails = CallRest<SubscriptionDetails>(parameters...)
var myContactDetails = CallRest<ContactDetails>(parameters...)
var myInvoiceDetails = CallRest<InvoiceDetails>(parameters...)

Open in new window


From your reply are you suggesting this would be implemented like this?

OrgDetails myOrgDetails = RestRetrieveOrgDetails(parameters...);
OrderDetails myOrderDetails = RestRetrieveOrderDetails(parameters...);
ItemDetails myItemDetails = RestRetrieveItemDetails(parameters...); 
etc

Open in new window

Since there is quite a lot of logic in the CallRest function (I've not listed it all, but it supports Put, Get, Delete, Post, Patch etc) are you suggesting that this logic be duplicated in each of these calls? It doesn't seem a good design.
Correct. I would have those calls in the consumer (business layer or higher).

Since there is quite a lot of logic in the CallRest function (I've not listed it all, but it supports Put, Get, Delete, Post, Patch etc) are you suggesting that this logic be duplicated in each of these calls? It doesn't seem a good design.
In OOP you don't have redundant code. But having similar interfaces for different entities is pretty common.
Another drawback of your solution is coupling. You'll get much more complex methods,

var myContactDetails = CallRest<ContactDetails>(parameters...)
var myInvoiceDetails = CallRest<InvoiceDetails>(parameters...)

Open in new window

Consider a modular business, where contacts are CRM and invoces are finance. How do you create a CRM module using contacts without refernence to the invoces. The answer is you cannot. Cause your "generic method" has coupled internals.

Also to mention, those methods and classes can often be created automatically by using T4.

Since there is quite a lot of logic in the CallRest function (I've not listed it all, but it supports Put, Get, Delete, Post, Patch etc) are you suggesting that this logic be duplicated in each of these calls?
No. The (possible abstract) basic calls work only on strings as input and output data. Types should only be necessary on higher levels. You need to reconsider what the basic calls must cover (normally): Authentication, error handling and using string data with the actual HTTP verbs.
Sorry I still don't understand what you are saying. I have three main questions.

1. If we are creating all these separate methods and don't have a single CallRest method, then how are these separate methods not duplicating the REST calls?
2. Regarding a modular business model, typically there are "core" basic functions such as logging, authentication, REST calls etc which all business modules make use of, a modular model wouldn't implement each of these individually which is what would happen with the duplicated REST calls in each typed method.
3. Regarding the comment "your "generic method" has coupled internals" comment - if there are no types in the REST method but each type is passed in through generics, how is any coupling taking place?
Well, can you post a concrete, concise example. The problem is I have none at hands to show you the idea.

1) Does your REST API provides only one method? If not, then you should not try to stuff your calling methods into one. This is an architectural mismatch.

2) Depending on your kind of REST API, it points to different entities. Thus your data access layer needs to have different methods for different entities.

3) That is the exact point. There should be none. The layer above the abstract, core REST handling, when it exists, does that by converting the strings used by the API into the correct types and vice versa.
Thank you for continuing to unpack what you are saying. Unfortunately I'm not getting anywhere with your replies.

1) Does your REST API provides only one method? If not, then you should not try to stuff your calling methods into one. This is an architectural mismatch.

But isn't the code now going to get a lot of duplication? Now each REST call has to perform the exact same checks for building up a standard REST call?

2) Depending on your kind of REST API, it points to different entities. Thus your data access layer needs to have different methods for different entities.

It does have different methods for different entities - they are passed through using generics.

3) That is the exact point. There should be none. The layer above the abstract, core REST handling, when it exists, does that by converting the strings used by the API into the correct types and vice versa.

I don't understand. Earlier you said "your "generic method" has coupled internals" - I asked how that could be using generics - I'm not clear how the above explanation has answered my question.
Please post a concrete, concise example. The problem is I have none at hands to show you the idea.

Especially as doing it own my own would be the work of a term paper. Thus we need some concrete code, some concrete API calls to narrow to problem down to your use-cases.
I did post an example and we have been discussing it. I feel it would be better if you just pointed me at a course or paper or exponent of the idea you are trying to convey to me and I will study it further. At present I am no clearer about the point you have been trying to make than when we started and I don't really anticipate this changing.