根据类型参数调用泛型方法中的不同方法

本文关键字:方法 泛型方法 类型参数 调用 | 更新日期: 2023-09-27 18:18:44

我有一些这样的方法:

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为每个类型?

根据类型参数调用泛型方法中的不同方法

您可以使用强制转换,但它很难看:

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

(等)

老实说,这种事情最终总是相当丑陋的。一种选择是创建一个像这样的Dictionary<Type, Delegate>:

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;
}

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

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; }
    }
}

在我的例子中,我可以替换a:

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; }            
}
参考:

  • c#:在异构字典
  • 上暴露类型安全API
  • https://stackoverflow.com/a/2178769/136285