简体   繁体   English

通用C#类:设置“通用”属性

[英]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. 我对C#很陌生,所以我可能会遇到C#有一个简单解决方案的问题。 I have a generic class with a property of "generic" type. 我有一个泛型类,属性为“generic”类型。 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. 它甚至没有编译,因为如果Data是 - 例如 - 布尔值,则“Data = new string []”不能工作。

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: 由于这种行为是根据T专门设计的,因此您应该使您的泛型类成为一个抽象基础,从中派生专门的子类:

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 *. 顺便说一下,当Action *或Func *形式的相同签名已经可用时,通常不需要定义自己的委托。 In the case of your StringToStatusCode , this could simply be defined as a Func<string, StatusCode> . 对于StringToStatusCode ,可以简单地将其定义为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()方法:

Convert.ChangeType( input, typeof(T) );

but this will only work for the types that the Convert class is aware of. 但这只适用于Convert类知道的类型。 Conversions to most custom types just will fail with a InvalidCastException . 转换为大多数自定义类型只会因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. 如果您确实需要支持将任意输入从string转换为某种类型T ,则应该提供单独的泛型函数作为可以执行转换的类型的参数。 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. 一种适用于简单类型的方法是使用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... 这样可以避免使用类型转换器,但可能会使你的类继承链变得过于复杂......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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