如何使方法的返回类型变成通用类型?

219

有没有办法将这个方法变成通用的,这样我就可以返回字符串、布尔值、整数或双精度浮点数了?目前,它返回一个字符串,但如果它能够找到 "true" 或 "false" 作为配置值,我希望例如返回布尔值。

    public static string ConfigSetting(string settingName)
    {  
         return ConfigurationManager.AppSettings[settingName];
    }

1
你有办法知道每个设置的类型是什么吗? - thecoshman
2
我认为你真正想问的问题是“如何使我的应用程序配置强类型化?”不过,我已经很久没有涉足这方面了,无法给出一个恰当的答案。 - Simon
是的,理想情况下我不想在方法中传递类型。我只会有我提到的4种类型。所以如果设置了“true”/“false”,我希望这个函数返回一个布尔值(无需将其传递到方法中),我可能可以将int和double合并为double,并且其他所有内容都应该是字符串。已经回答的内容可以正常工作,但我需要每次传递类型,这也许没问题。 - Entree
3
你的评论听起来像是在问一个方法,它将根据实际检索到的设置名称键的数据,在运行时返回一个强类型的 bool(或 string、int等)。C#不会为你做到这一点;你无法在编译时知道该值的类型。换句话说,这是动态类型,而不是静态类型。如果你使用 dynamic 关键字,C#可以为你完成这个操作。虽然这样会有性能成本,但对于读取配置文件来说,性能成本几乎可以忽略不计。 - phoog
7个回答

459
您需要将其改为一个通用方法,像这样:

public static T ConfigSetting<T>(string settingName)
{  
    return /* code to convert the setting to T... */
}

但是调用方必须指定他们所期望的类型。你可以潜在地使用Convert.ChangeType,假设所有相关类型都得到支持:

public static T ConfigSetting<T>(string settingName)
{  
    object value = ConfigurationManager.AppSettings[settingName];
    return (T) Convert.ChangeType(value, typeof(T));
}

我并不完全相信这一切都是一个好主意,你知道的...


24
/将设置转换为T的代码.../接下来是整个小说 :) - Adrian Iftode
2
@thecoshman:这样做也可以,但如果你不这样做的话,你会拿返回值做什么? - George Duckett
6
虽然这个答案当然是正确的,并且正如你所指出的满足了提问者的要求,但可能值得一提的是,旧的分离方法(例如ConfigSettingStringConfigSettingBool等)的优点在于方法体会更短、更清晰、更专注。 - phoog
6
如果不建议这样做,那么通用返回类型的目的是什么? - bobbyalex
1
@phoog,您能否回答以展示您的意思? - Entree
显示剩余12条评论

37
您可以使用 Convert.ChangeType() 方法:
public static T ConfigSetting<T>(string settingName)
{
    return (T)Convert.ChangeType(ConfigurationManager.AppSettings[settingName], typeof(T));
}

27

有许多方法可以做到这一点(按优先级列出,特定于原帖的问题)

  1. Option 1: Straight approach - Create multiple functions for each type you expect rather than having one generic function.

    public static bool ConfigSettingInt(string settingName)
    {  
         return Convert.ToBoolean(ConfigurationManager.AppSettings[settingName]);
    }
    
  2. Option 2: When you don't want to use fancy methods of conversion - Cast the value to object and then to generic type.

    public static T ConfigSetting<T>(string settingName)
    {  
         return (T)(object)ConfigurationManager.AppSettings[settingName];
    }
    

    Note - This will throw an error if the cast is not valid(your case). I would not recommend doing this if you are not sure about the type casting, rather go for option 3.

  3. Option 3: Generic with type safety - Create a generic function to handle type conversion.

    public static T ConvertValue<T,U>(U value) where U : IConvertible
    {
        return (T)Convert.ChangeType(value, typeof(T));
    } 
    

    Note - T is the expected type, note the where constraint here(type of U must be IConvertible to save us from the errors)


8
为什么在 U 中要将第三个选项定义成通用类型?这样做没有意义,而且使方法的调用更加困难。只需接受 IConvertible 即可。我认为考虑到它不能回答被问的问题,这个问题不值得包括第二个选项。你可能还需要重命名第一个选项中的方法... - Jon Skeet

9
你需要将方法返回值的类型转换为调用该方法时传递的泛型类型。
    public static T values<T>()
    {
        Random random = new Random();
        int number = random.Next(1, 4);
        return (T)Convert.ChangeType(number, typeof(T));
    }

您需要传递一个可以转换为该方法返回值类型的类型。如果您想返回一个不能转换为您传递的泛型类型的值,则可能需要修改代码或确保传递一个可以转换为方法返回值的类型。因此,不建议采用这种方法。

完全正确 - 对我来说,最后一行 return (T)Convert.ChangeType(number, typeof(T)); 恰恰是我所缺少的 - 干杯! - Greg Trevellick

5
创建一个函数并将输出参数作为通用类型传递。
 public static T some_function<T>(T output_object /*declare as Output object*/)
    {
        return output_object;
    }

对于某些用例来说,这实际上非常聪明,比如从数据库中提取数据。你知道你会得到一个T类型的数据列表。加载方法并不知道你现在想要哪种类型的T。因此,只需将一个新的List<WantedObject>传递给它,方法就可以完成工作并在返回之前填充列表。真不错! - Marco Heumann

2
请尝试以下代码:

请尝试以下代码:

public T? GetParsedOrDefaultValue<T>(string valueToParse) where T : struct, IComparable
{
 if(string.EmptyOrNull(valueToParse))return null;
  try
  {
     // return parsed value
     return (T) Convert.ChangeType(valueToParse, typeof(T));
  }
  catch(Exception)
  {
   //default as null value
   return null;
  }
 return null;
}

0
 private static T[] prepareArray<T>(T[] arrayToCopy, T value)
    {
        Array.Copy(arrayToCopy, 1, arrayToCopy, 0, arrayToCopy.Length - 1);
        arrayToCopy[arrayToCopy.Length - 1] = value;
        return (T[])arrayToCopy;
    }

我在我的代码中一直在执行这个操作,想要将其放入一个方法中。我想在这里分享这个方法,因为我不必使用Convert.ChangeType来返回值。这可能不是最佳实践,但对我来说有效。该方法接受一个泛型类型的数组和一个要添加到数组末尾的值。然后复制该数组并剥离第一个值,将传入方法的值添加到数组末尾。最后,我返回泛型数组。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接