C#中的变量泛型返回类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11567300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 18:22:13  来源:igfitidea点击:

Variable generic return type in C#

c#generics

提问by richzilla

Is there any way to have a method return any one of a number of generic types from a method? For example, I have the following:

有没有办法让方法从方法返回多种泛型类型中的任何一种?例如,我有以下内容:

public static T ParseAttributeValue<T>(this XElement element, string attribute)
    {
        if(typeof(T) == typeof(Int32))
        {
            return Int32.Parse(element.Attribute(attribute).Value);
        }

        if(typeof(T) == typeof(Double))
        {
            return Double.Parse(element.Attribute(attribute).Value);
        }

        if(typeof(T) == typeof(String))
        {
            return element.Attribute(attribute).Value;
        }

        if(typeof(T) == typeof(ItemLookupType))
        {
            return Enum.Parse(typeof(T), element.Attribute(attribute).Value);
        }
    }

(This is only a very quick mockup, I'm aware that any production code would need to be significantly more thorough in null checks etc...)

(这只是一个非常快速的模型,我知道任何生产代码都需要在空检查等方面更加彻底......)

But the compiler doesn't like it, complaining that Int32cannot be implicitly converted to T(it doesn't work with a cast either). I can understand that. At compile time it has no way to know what Tis, but I'm checking it beforehand. Is there anyway I can make this work?

但是编译器不喜欢它,抱怨Int32不能隐式转换为T(它也不适用于强制转换)。我能理解。在编译时它无法知道是什么T,但我事先检查了它。无论如何我可以使这项工作?

采纳答案by Joshua

I've done these types of generic methods in the past. The easiest way to get type inference is to provide a generic converter function.

我过去做过这些类型的通用方法。获得类型推断的最简单方法是提供通用转换器函数。

public static T ParseAttributeValue<T>
          (this XElement element, string attribute, Func<string, T> converter)
{
  string value = element.Attribute(attribute).Value;
  if (String.IsNullOrWhiteSpace(value)) {
    return default(T);
  }

  return converter(value);
}

You can use it like the following:

您可以像下面这样使用它:

int index = element.ParseAttributeValue("index", Convert.ToInt32);
double price = element.ParseAttributeValue("price", Convert.ToDouble);

You can even provide your own functions and have all the fun in the world (even return anonymous types):

您甚至可以提供自己的函数并享受世界上所有的乐趣(甚至返回匿名类型):

ItemLookupType lookupType = element.ParseAttributeValue("lookupType",
  value => Enum.Parse(typeof(ItemLookupType), value));

var item = element.ParseAttributeValue("items",
  value => {
    List<string> items = new List<string>();
    items.AddRange(value.Split(new [] { ',' }));
    return items;
  });

回答by Chris Shain

Why are you using the type parameter as the return type at all? This would work, just requires a cast after calling:

为什么要使用类型参数作为返回类型?这会起作用,只需要在调用后进行强制转换:

public static Object ParseAttributeValue<T>(this XElement element, string attribute)
{
    if(typeof(T) == typeof(Int32))
    {
        return Int32.Parse(element.Attribute(attribute).Value);
    }

    if(typeof(T) == typeof(Double))
    {
        return Double.Parse(element.Attribute(attribute).Value);
    }

    if(typeof(T) == typeof(String))
    {
        return element.Attribute(attribute).Value;
    }

    if(typeof(T) == typeof(ItemLookupType))
    {
        return Enum.Parse(typeof(T), element.Attribute(attribute).Value);
    }
}

Or better yet:

或者更好:

public static Int32 ParseAsInt32(this XElement element, string attribute)
{
    return Int32.Parse(element.Attribute(attribute).Value);
}

// etc, repeat for each type

This second approach has the additional benefit of having a much higher likelihood of getting inlined, plus it will (for value types like Int32) prevent the need to box/unbox the value. Both of these will cause the method to perform somewhat faster.

第二种方法有一个额外的好处,即具有更高的内联可能性,而且它(对于像 Int32 这样的值类型)可以防止需要对值进行装箱/拆箱。这两者都会使方法执行得更快一些。

回答by CodingWithSpike

Not sure if this is exactly what you want, but you can make the returns work if you cast to objectfirst then to T

不确定这是否正是您想要的,但是如果您object首先转换为,则可以使返回工作T

    public static T ParseAttributeValue<T>(this XElement element, string attribute)
    {
        if (typeof(T) == typeof(Int32))
        {
            return (T)(object)Int32.Parse(element.Attribute(attribute).Value);
        }

        if (typeof(T) == typeof(Double))
        {
            return (T)(object)Double.Parse(element.Attribute(attribute).Value);
        }

        if (typeof(T) == typeof(String))
        {
            return (T)(object)element.Attribute(attribute).Value;
        }

        return default(T);
    }

However you still have to provide Tat compile time, calling the method like:

但是,您仍然必须T在编译时提供,调用如下方法:

int value = element.ParseAttributeValue<int>("attribute");

回答by MrWednesday

Here's two ways of doing it...

这里有两种方法可以做到...

    static T ReadSetting<T>(string value)
    {
        object valueObj = null;
        if (typeof(T) == typeof(Int32))
            valueObj = Int32.Parse(value);
        return (T)valueObj;
    }
    static dynamic ReadSetting2<T>(string value)
    {
        if (typeof(T) == typeof(Int32))
            return Int32.Parse(value);
        throw new UnsupportedException("Type is unsupported");
    }
    static void Main(string[] args)
    {
        int val1 = ReadSetting<Int32>("2");
        int val2 = ReadSetting2<Int32>("3");
    }

回答by Dennis

With C++ templates, this kind of thing would work, but only if each piece of code were in a different, separate specialization. The thing that makes that work is that unused function templates are not compiled (or more accurately: not fully instantiated), so the fact that a piece of code would be invalid if that copy of the template were instantiated with a different type doesn't come up.

使用 C++ 模板,这种事情会起作用,但前提是每段代码都在不同的、单独的专业化中。使该工作有效的原因是未编译未使用的函数模板(或更准确地说:未完全实例化),因此如果模板的副本使用不同类型实例化,则一段代码将无效这一事实不会上来。

C# is different, and AFAIK there's no specialization for generics. One way to accomplish what you are trying to do, while working within the limitations of C# would be to create one function with a more abstract return type, and use the ParseAttributeValue only to cast it to T.

C# 是不同的,AFAIK 没有泛型的专业化。在 C# 的限制范围内完成工作的一种方法是创建一个具有更抽象返回类型的函数,并仅使用 ParseAttributeValue 将其强制转换为 T。

So you would have:

所以你会有:

private static Object AbstractParseValue(System.Type t, XElement element, string attribute)

and

public static T ParseAttributeValue<T>(this XElement element, string attribute)
{
     return (T)AbstractParseValue(typeof(T), element, attribute);
}

回答by Monroe Thomas

.Net already has a bunch of great string conversion routines you can use! A TypeConvertercan do most of the heavy lifting for you. Then you don't have to worry providing your own parsing implementations for built-in types.

.Net 已经有一堆很棒的字符串转换例程可以使用!ATypeConverter可以为您完成大部分繁重的工作。然后您不必担心为内置类型提供自己的解析实现。

Note that there are locale-aware versions of the APIs on TypeConverterthat could be used if you need to handle parsing values expressed in different cultures.

请注意,TypeConverter如果您需要处理以不同文化表达的解析值,则可以使用API 的区域感知版本。

The following code will parse values using the default culture:

以下代码将使用默认区域性解析值:

using System.ComponentModel;

public static T ParseAttributeValue<T>(this XElement element, string attribute)
{
    var converter = TypeDescriptor.GetConverter(typeof(T));
    if (converter.CanConvertFrom(typeof(string)))
    {
        string value = element.Attribute(attribute).Value;
        return (T)converter.ConvertFromString(value);
    }

    return default(T);
}

This will work for a lot of built-in types, and you can decorate custom types with a TypeConverterAttributeto allow them to participate in the type conversion game too. This means that in the future you will be able to parse new types without having to change the implementation of the ParseAttributeValue.

这将适用于许多内置类型,您可以用 装饰自定义类型,TypeConverterAttribute让它们也参与类型转换游戏。这意味着将来您将能够解析新类型而无需更改ParseAttributeValue.

see: http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx

请参阅:http: //msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx

回答by supercat

I would suggest that rather than testing the type parameter every time the routine is executed, you should create a generic static class something like this:

我建议与其在每次执行例程时都测试类型参数,不如创建一个像这样的通用静态类:

internal static class ElementParser<T>
{
  public static Func<XElement, string, T> Convert = InitConvert;

  T DefaultConvert(XElement element, string attribute)
  {
    return Default(T); // Or maybe throw exception, or whatever
  }

  T InitConvert(XElement element, string attribute)
  {
    if (ElementParser<int>.Convert == ElementParser<int>.InitConvert)
    {  // First time here for any type at all
      Convert = DefaultConvert; // May overwrite this assignment below
      ElementParser<int>.Convert =
        (XElement element, string attribute) =>
          Int32.Parse(element.Attribute(attribute).Value);
      ElementParser<double>.Convert =
        (XElement element, string attribute) =>
          Int32.Parse(element.Attribute(attribute).Value);
      // etc. for other types
    }
    else // We've done other types, but not this type, and we don't do anything nice for it
    {
      Convert = DefaultConvert;
    }
    return Convert(element, attribute);      
  }
}
public static T ParseAttributeValue(this XElement element, string attribute)
{
  ElementParser<T>.Convert(element, attribute);
}

Using this approach, one will only have to do special handling the first time a particular type is used. After that, the conversion can be performed using only a single generic delegate invocation. Once could easily add any number of types, and even allow converters to be registered for any desired type at runtime.

使用这种方法,只需在第一次使用特定类型时进行特殊处理。之后,可以仅使用单个通用委托调用来执行转换。一次可以轻松添加任意数量的类型,甚至允许在运行时为任何所需类型注册转换器。