【问题标题】:Call different methods in a generic method based on the type parameter根据类型参数在泛型方法中调用不同的方法
【发布时间】:2014-08-20 08:25:47
【问题描述】:

我有几个这样的方法:

public string GetStringValue(string field) { /* ... */ }
public int GetIntValue(string field) { /* ... */ }

现在我想编写一个具有以下签名的通用方法:

public bool CopyValue<T>(string field, Action<T> copyAction)

根据类型参数,我想使用其中一种非泛型方法的返回值调用copyAction。我的第一次尝试是

public bool CopyValue<T>(string field, Action<T> copyAction)
{
    if (typeof(T) == typeof(string))
        copyAction((GetStringValue(field));
    else if (typeof(T) == typof(int))
        copyAction(GetIntValue(field));
    else
        return false;

    return true;
}

但这甚至无法编译。然后我尝试将我的非通用方法包装在通用方法中,例如

public string GetValue<string>(string field)
{
    return GetStringValue(field);
}

显然也不能编译。

可以这样做还是我必须为每种类型显式实现CopyValue

【问题讨论】:

  • 如果你需要知道泛型方法中的类型,你就是在滥用泛型。
  • typof(int)) - 应该是typeof。此外,您需要像copyAction((T)GetIntValue(field)); 一样投射。除此之外,应该可以工作。
  • 您可能应该为每种支持的类型提供多个重载。

标签: c# .net generics


【解决方案1】:

你可以让它与铸造一起工作,但它很丑:

if (typeof(T) == typeof(string))
{
    copyAction((T)(object) GetStringValue(field));
}

(等)

说实话,这种事情最终总是相当丑陋。一种选择是像这样创建Dictionary&lt;Type, Delegate&gt;

Dictionary<Type, Delegate> fieldExtractors = new Dictionary<Type, Delegate>
{
    { typeof(string), (Func<string, string>) field => GetStringValue(field) },
    { typeof(int), (Func<string, int>) field => GetIntValue(field) },
};

那么你可以使用:

public bool CopyValue<T>(string field, Action<T> copyAction)
{
    Delegate fieldExtractor;
    if (fieldExtractors.TryGetValue(typeof(T), out fieldExtractor))
    {
        var extractor = (Func<string, T>) fieldExtractor;
        copyAction(extractor(field));
        return true;
    }
    return false;
}

【讨论】:

    【解决方案2】:

    如果您可以重构一些代码,您可以使用这个技巧。最大的好处是这是在编译时而不是在运行时完成的:

    public class JsonDictionary
    {
        public static readonly Key<int> Foo = new Key<int> { Name = "FOO" };
        public static readonly Key<string> Bar = new Key<string> { Name = "BAR" };
            
        IDictionary<string, object> _data;
        public JsonDictionary()
        {
            _data = new Dictionary<string, object>();
        }
        
        public void Set<T>(Key<T> key, T obj)
        {
            _data[key.Name] = obj;
        }
    
        public T Get<T>(Key<T> key)
        {
            return (T)_data[key.Name];
        }
        
        public class Key<T>
        {
            public string Name { get; init; }
        }
    }
    

    在我的情况下,我能够替换:

    Dictionary<Type, Delegate> jsonElementDelegates = new Dictionary<Type, Delegate>
    {
        { typeof(string), (Func<JsonElement, string>) (arrayItem => arrayItem.GetString()!) },
        { typeof(float), (Func<JsonElement, float>)(arrayItem => arrayItem.GetSingle()) },
        { typeof(double), (Func<JsonElement, double>) (arrayItem => arrayItem.GetDouble()) },
        { typeof(short), (Func<JsonElement, short>)(arrayItem => arrayItem.GetInt16()) },
        { typeof(ushort), (Func<JsonElement, ushort>) (arrayItem => arrayItem.GetUInt16()) },
        { typeof(int), (Func<JsonElement, int>)(arrayItem => arrayItem.GetInt32()) },
        { typeof(uint), (Func<JsonElement, uint>) (arrayItem => arrayItem.GetUInt32()) },
        { typeof(long), (Func<JsonElement, long>)(arrayItem => arrayItem.GetInt64()) },
        { typeof(ulong), (Func<JsonElement, ulong>) (arrayItem => arrayItem.GetUInt64()) },
    };
    

    使用编译时等效项:

    public static readonly Converter<string> ConvString = new Converter<string> { Fun = arrayItem => arrayItem.GetString()! };
    public static readonly Converter<float> ConvFloat = new Converter<float> { Fun = arrayItem => arrayItem.GetSingle() };
    public static readonly Converter<double> ConvDouble = new Converter<double> { Fun = arrayItem => arrayItem.GetDouble() };
    public static readonly Converter<short> ConvShort = new Converter<short> { Fun = arrayItem => arrayItem.GetInt16() };
    public static readonly Converter<ushort> ConvUShort = new Converter<ushort> { Fun = arrayItem => arrayItem.GetUInt16() };
    public static readonly Converter<int> ConvInt = new Converter<int> { Fun = arrayItem => arrayItem.GetInt32() };
    public static readonly Converter<uint> ConvUInt = new Converter<uint> { Fun = arrayItem => arrayItem.GetUInt32() };
    public static readonly Converter<long> ConvLong = new Converter<long> { Fun = arrayItem => arrayItem.GetInt64() };
    public static readonly Converter<ulong> ConvULong= new Converter<ulong> { Fun = arrayItem => arrayItem.GetUInt64() };
    public class Converter<T>
    {
        public Func<JsonElement, T> Fun { get; init; }            
    }
    

    参考:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-12
      • 1970-01-01
      • 1970-01-01
      • 2011-04-27
      • 2010-09-16
      • 2016-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多