Generic C# Class: Set "Generic" Property
I'm quite new to C#, so I might have a problem that C# has a simple solution for. I have a generic class with a property of "generic" type. I want to have a function to set that property, but I need to convert it to do so.
public class BIWebServiceResult<T>
{
public T Data;
public delegate StatusCode StringToStatusCode(string Input);
public void SetData(string Input, StringToStatusCode StringToError)
{
if (StringToError(Input) == 0)
{
if (Data is string[])
{
Data = new string[1];
Data[0] = Input;
}
else if (Data is string)
{
开发者_运维问答 Data = Input;
}
else if (Data is bool)
{
Data = DetectBool(Input);
}
}
}
private bool DetectBool(string Compare)
{
return Compare == "true";
}
}
The problem with that approach is, that it does not work :)
(No that's not all code, just a snippet to show what my problem is)
It doesn't even compile, because "Data = new string[]" can't work if Data is - for example - boolean.
How do I implement a function that behaves differently depending on the type of my generic property?
You want a generic class, but you're changing its behavior based on its generic type argument.
Since this behavior is specialized according to T
, you should really make your generic class an abstract base from which to derive specialized subclasses:
public abstract class BIWebServiceResult<T>
{
public T Data { get; set; }
public delegate StatusCode StringToStatusCode(string Input);
public abstract void SetData(string Input, StringToStatusCode StringToError);
}
Then you might have, for example:
public class BIWebServiceStrArrayResult : BIWebServiceResult<string[]>
{
public override void SetData(string Input, StringToStatusCode StringToError)
{
if (StringToError(Input) == 0)
{
Data = new string[1];
Data[0] = Input;
}
}
}
Personally, though, I'd be inclined to do away with all this manual string manipulation altogether and leave the job of parsing input to whatever code is calling this method:
// This is the same signature used by, e.g., int.TryParse, double.TryParse, etc.
public delegate bool Parser<T>(string input, out T output);
public void SetData(string Input, Parser<T> parser)
{
T value;
if (parser(Input, out value))
Data = value;
}
By the way, typically it's not really necessary to define your own delegates when the same signature is already available in the form of an Action
* or Func
*. In the case of your StringToStatusCode
, this could simply be defined as a Func<string, StatusCode>
. (But I would still personally recommend something like the last bit of code I posted instead.)
You could try using the Convert.ChangeType()
method:
Convert.ChangeType( input, typeof(T) );
but this will only work for the types that the Convert
class is aware of. Conversions to most custom types just will fail with a InvalidCastException
.
As a general pratice, this is not a good way to structure a generic class. Generics are meant to unify types based on a common interface. In your case, that common interface is that you expect a conversion from a string representation to the generic type.
If you really need to support conversion of arbitrary input from string
to some type T
you should provide a separate generic function as a parameter to the type that can perform the conversion. Here's an example:
class BIWebServiceResult<T>
{
private readonly Func<string,T> m_ValueParser;
public BIWebServiceResult( Func<string,T> valueParser )
{
m_ValueParser = valueParser;
}
public void SetData(string Input, StringToStatusCode StringToError)
{
Data = m_ValueParser( Input ); // use supplied conversion func
//...
}
}
An approach that will work for simple types is to use a TypeConverter.
T value = default(T);
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
if (converter != null)
{
if (converter.CanConvertFrom(typeof(string))
{
value = (T)converter.ConvertFrom(myString);
}
}
Hard to say if this would make much sense in your scenario, but you could perhaps use a child class for each of the possible data types, somewhat like:
public abstract class BIWebServiceResult<T>
{
public T Data;
public delegate void StringToStatusCode(string Input);
public abstract void SetData(string Input, StringToStatusCode StringToError);
}
public class StringBIServiceResult : BIWebServiceResult<string[]>
{
public override void SetData(string Input, StringToStatusCode StringToError)
{
Data = new string[1];
Data[0] = Input;
}
private bool DetectBool(string Compare)
{
return Compare == "true";
}
}
this would avoid the casting and using type converters, but might be make your class inheritance chain unduly complex...
精彩评论