[.net] Trying to Implement Generic Method

Started by
2 comments, last by freddyscoming4you 12 years, 7 months ago
I have implemented an interface to be used by all certain types of a class which represents a response to a WebRequest. I have the interface implement a method whose return type is also the interface so I can get a return value to be the parsed response. The parsing of the response is handled in the interface implementation in each class, naturally. However, I'm having difficulty in implementing this as I don't have hardly any experience writing generic methods and I'm not having much help using Google. Below is my class implementation, or what I have managed to cobble together so far.

Can you see what's off?

Thanks.

First, the interface declarations:



using System.Xml;

namespace EVE_API.Interfaces
{
public interface IAPIResponse
{
IAPIResponse ParseResponse(XmlDocument response);
}
}



using System.Collections.Generic;
using System.Xml;

namespace EVE_API.Interfaces
{
public interface IEveAPI
{
string ApiName { get; }

Dictionary<string,string> Arguments { get; set; }
}
}


Now, the response class:



using System;
using System.Xml;
using EVE_API.Interfaces;

namespace EVE_API.APIs.Miscelleneous
{
public class ServerStatusResponse : IAPIResponse
{
private string _version;
private DateTime _currentTime;
private bool _open;
private int _onlinePlayers;
private DateTime _cachedUntil;

public string Version { get { return _version; } }
public DateTime CurrentTime { get { return _currentTime; } }
public bool Open { get { return _open; } }
public int OnlinePlayers { get { return _onlinePlayers; } }
public DateTime CachedUntil { get { return _cachedUntil; } }

private ServerStatusResponse(XmlDocument responseDocument)
{
_version = responseDocument.SelectSingleNode("/eveapi").Attributes["version"].Value;

DateTime dtTry = DateTime.MinValue;

DateTime.TryParse(responseDocument.SelectSingleNode("/eveapi/currentTime").Value, out dtTry);

if (dtTry != DateTime.MinValue)
{
_currentTime = dtTry;
}
else
{
throw new MalformedResponseException("Could not find currentTime in response from SeverStatus API.");
}

bool boolTry;

bool.TryParse(responseDocument.SelectSingleNode("/eveapi/result/open").Value, out boolTry);

_open = boolTry;

int intTry;

int.TryParse(responseDocument.SelectSingleNode("/eveapie/result/onlinePlayers").Value, out intTry);

if (intTry > int.MinValue)
{
_onlinePlayers = intTry;
}
else
{
throw new MalformedResponseException("Could not find onlinePlayers in response from SeverStatus API.");
}

dtTry = DateTime.MinValue;

DateTime.TryParse(responseDocument.SelectSingleNode("/eveapi/result/cachedUntil").Value, out dtTry);

if (dtTry != DateTime.MinValue)
{
_cachedUntil = dtTry;
}
else
{
throw new MalformedResponseException("Could not find cachedUntil in response from SeverStatus API.");
}
}

public IAPIResponse ParseResponse(XmlDocument response)
{
return new ServerStatusResponse(response);
}
}
}


And now the class trying to wrap all the above together



using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Xml;
using EVE_API.Interfaces;

namespace EVE_API
{
public class APICaller<T> where T : IAPIResponse
{
public APICaller()
{
}

public T ExecuteAPICall<T>(IEveAPI api)
{
List<string> urlParts = new List<string>();

urlParts.Add(api.ApiName);

if (api.Arguments != null)
{
urlParts.Add("?");

var keys = api.Arguments.Keys;

foreach (string key in keys)
{
urlParts.Add(key);
urlParts.Add("=");
urlParts.Add(api.Arguments[key]);
urlParts.Add("&");
}

urlParts.RemoveAt(urlParts.Count - 1);
}

string completeUrl = urlParts.ToString();

WebRequest request = WebRequest.Create(completeUrl);
WebResponse response = request.GetResponse();

XmlDocument document = new XmlDocument();
document.Load(response.GetResponseStream());

IAPIResponse apiResponse = new (IAPIResponse)T();

return apiResponse.ParseResponse(document);
}
}
}
Advertisement
if you dont know about generics... why would you put that into your code? you can easily code it without.
if you really want to learn about generics.. buy a C# book, study it.. implement some single examples and, when you feel confident enough with it you'll have your big chance to use it in your producion code.

Stefano Casillo
TWITTER: [twitter]KunosStefano[/twitter]
AssettoCorsa - netKar PRO - Kunos Simulazioni

[font="Arial"] I'm not sure I see the purpose of [/font][font="Arial"][font="Courier New"]ParseResponse[/font] as a factory-like instance method. If you already have an instance of the concrete type you want (i.e. [/font][font="Arial"][font="Courier New"]ServerStatusResponse[/font]), why would you create a clone? As it stands right now, your generic method creates a default-initialized instance of a particular response, and then promptly throws it away by having it return a new instance of that response. I also don't believe the generic method is necessary here since your code doesn't require concrete type information -- it can just use regular inheritence by having the user pass in their own response object:


public interface IAPIResponse
{
void ParseResponse(XmlDocument response);
}

public class ServerStatusResponse : IAPIResponse
{
void ParseResponse(XmlDocument response)
{
// ... all that code that was previously in the constructor ...
}
}

public class APICaller
{
public IAPIResponse ExecuteAPICall(IEveAPI api, IAPIResponse response)
{
// ... stuff.
response.ParseResponse(document);
return response;
}
}


If you want to take this one step further, which IMHO you should, then don't even work with concrete types outside of these methods at all and rely on factories to create the correct response type:


public class APICaller
{
private IAPIResponse CreateResponse(XmlDocument rawResponse)
{
string type;
// ... code that populates "type" based on raw response data
if(type == "ServerStatus")
{
return new ServerStatusResponse(rawResponse);
}

return null;
// or even throw exception
}

public IAPIResponse ExecuteAPICall(IEveAPI api)
{
// ... get XmlDocument response ...
return CreateResponse(document);
}
}
[/font]

if you dont know about generics... why would you put that into your code? you can easily code it without.
if you really want to learn about generics.. buy a C# book, study it.. implement some single examples and, when you feel confident enough with it you'll have your big chance to use it in your producion code.



I started coding that around 12:30 and I was trying to think of a generic way to use interfaces and generics to create a singular method that I could use to execute API calls since they all follow the same pattern and my mind shouted "GENERICS!" And, so it was, or wasn't. Haha.


[font="Arial"] I'm not sure I see the purpose of [/font][font="Arial"][font="Courier New"]ParseResponse[/font] as a factory-like instance method. If you already have an instance of the concrete type you want (i.e. [/font][font="Arial"][font="Courier New"]ServerStatusResponse[/font]), why would you create a clone? As it stands right now, your generic method creates a default-initialized instance of a particular response, and then promptly throws it away by having it return a new instance of that response. I also don't believe the generic method is necessary here since your code doesn't require concrete type information -- it can just use regular inheritence by having the user pass in their own response object:
[/font]


Thanks for the suggestion. I stripped out all the generics stuff and simply passed in a IApiResponse object and return it's parse method from that. Subsequently I removed the parsing code out of the constructor and put it in the parse method which returns void. A few hours of sleep and a thought out suggestion will do wonders.

This topic is closed to new replies.

Advertisement