C# 中的泛型和访问 T 的静态成员

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19843/
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-01 09:03:48  来源:igfitidea点击:

Generics in c# & accessing the static members of T

提问by niklasfi

My question concerns c# and how to access Static memebers ... Well I don't really know how to explain it (wich kind of is bad for a question isn't it?) I will just give you some sample code:

我的问题涉及 c# 以及如何访问静态成员......好吧,我真的不知道如何解释它(这对问题来说很糟糕,不是吗?)我会给你一些示例代码:

Class test<T>{
     int method1(Obj Parameter1){
         //in here I want to do something which I would explain as
         T.TryParse(Parameter1);

         //my problem is that it does not work ... I get an error.
         //just to explain: if I declare test<int> (with type Integer)
         //I want my sample code to call int.TryParse(). If it were String
         //it should have been String.TryParse()
     }
}

So thank you guys for your answers (By the way the question is: how would I solve this problem without getting an error). This probably quite an easy question for you!

所以谢谢你们的回答(顺便说一下,问题是:我将如何解决这个问题而不会出错)。这对你来说可能是一个很简单的问题!

Thanks, Niklas

谢谢,尼克拉斯



Edit: Thank you all for your answers!

编辑:谢谢大家的回答!

Though I think the try - catch phrase is the most elegant, I know from my experience with vb that it can really be a bummer. I used it once and it took about 30 minutes to run a program, which later on only took 2 minutes to compute just because I avoided try - catch.

虽然我认为 try-catch 短语是最优雅的,但我从我的 vb 经验中知道它真的很糟糕。我用过一次,运行一个程序花了大约 30 分钟,后来只用了 2 分钟来计算,因为我避免了 try - catch。

This is why I chose the swich statement as the best answer. It makes the code more complicated but on the other hand I imagine it to be relatively fast and relatively easy to read. (Though I still think there should be a more elegant way ... maybe in the next language I learn :P )

这就是为什么我选择 swich 语句作为最佳答案的原因。它使代码更加复杂,但另一方面,我认为它相对较快且相对易于阅读。(虽然我仍然认为应该有一种更优雅的方式......也许在我学习的下一种语言中:P)



Though if you have some other suggestion I am still waiting (and willing to participate)

尽管如果您有其他建议,我仍在等待(并愿意参与)

采纳答案by Timbo

One more way to do it, this time some reflection in the mix:

还有一种方法可以做到这一点,这次是混合中的一些反思:

static class Parser
{
    public static bool TryParse<TType>( string str, out TType x )
    {
        // Get the type on that TryParse shall be called
        Type objType = typeof( TType );

        // Enumerate the methods of TType
        foreach( MethodInfo mi in objType.GetMethods() )
        {
            if( mi.Name == "TryParse" )
            {
                // We found a TryParse method, check for the 2-parameter-signature
                ParameterInfo[] pi = mi.GetParameters();
                if( pi.Length == 2 ) // Find TryParse( String, TType )
                {
                    // Build a parameter list for the call
                    object[] paramList = new object[2] { str, default( TType ) };

                    // Invoke the static method
                    object ret = objType.InvokeMember( "TryParse", BindingFlags.InvokeMethod, null, null, paramList );

                    // Get the output value from the parameter list
                    x = (TType)paramList[1];
                    return (bool)ret;
                }
            }
        }

        // Maybe we should throw an exception here, because we were unable to find the TryParse
        // method; this is not just a unable-to-parse error.

        x = default( TType );
        return false;
    }
}

The next step would be trying to implement

下一步将尝试实施

public static TRet CallStaticMethod<TRet>( object obj, string methodName, params object[] args );

With full parameter type matching etc.

具有全参数类型匹配等。

回答by Greg Hurlman

The problem is that TryParse isn't defined on an interface or base class anywhere, so you can't make an assumption that the type passed into your class will have that function. Unless you can contrain T in some way, you'll run into this a lot.

问题是 TryParse 未在任何地方的接口或基类上定义,因此您不能假设传递给您的类的类型将具有该功能。除非您能以某种方式限制 T,否则您会经常遇到这种情况。

Constraints on Type Parameters

类型参数的约束

回答by svrist

You probably cant do it.

你可能做不到。

First of all if it should be possible you would need a tighter bound on T so the typechecker could be sure that all possible substitutions for T actually had a static method called TryParse.

首先,如果可能的话,您需要对 T 进行更严格的限制,以便类型检查器可以确保 T 的所有可能替换实际上都有一个名为 TryParse 的静态方法。

回答by Nick Berardi

That is not how statics work. You have to think of statics as sort of in a Global class even if they are are spread across a whole bunch of types. My recommendation is to make it a property inside the T instance that can access the necessary static method.

静力学不是这样工作的。您必须将静态视为全局类中的一种,即使它们分布在一大堆类型中。我的建议是使它成为 T 实例中可以访问必要静态方法的属性。

Also T is an actual instance of something, and just like any other instance you are not able to access the statics for that type, through the instantiated value. Here is an example of what to do:

此外 T 是某物的实际实例,就像任何其他实例一样,您无法通过实例化值访问该类型的静态数据。这是一个如何做的例子:

class a {
    static StaticMethod1 ()
    virtual Method1 ()
}

class b : a {
    override Method1 () return StaticMethod1()
}

class c : a {
    override Method1 () return "XYZ"
}

class generic<T> 
    where T : a {
    void DoSomething () T.Method1()
}

回答by samjudson

To access a member of a specific class or interface you need to use the Where keyword and specify the interface or base class that has the method.

要访问特定类或接口的成员,您需要使用 Where 关键字并指定具有该方法的接口或基类。

In the above instance TryParse does not come from an interface or base class, so what you are trying to do above is not possible. Best just use Convert.ChangeType and a try/catch statement.

在上面的实例中 TryParse 不是来自接口或基类,所以你上面尝试做的事情是不可能的。最好只使用 Convert.ChangeType 和 try/catch 语句。

class test<T>
{
    T Method(object P)
    {
       try {
           return (T)Convert.ChangeType(P, typeof(T));
       } catch(Exception e) {
           return null;
       }
    }
}

回答by Rob Cooper

You may want to read my previous post on limiting generic types to primitives. This may give you some pointers in limiting the type that can be passed to the generic (since TypeParseis obviously only available to a set number of primitives ( string.TryParseobviously being the exception, which doesn't make sense).

您可能想阅读我之前关于将泛型类型限制为基元的文章。这可能会为您提供一些限制可以传递给泛型的类型的指针(因为TypeParse显然仅适用于一定数量的原语(string.TryParse显然是例外,这没有意义)。

Once you have more of a handle on the type, you can then work on trying to parse it. You may need a bit of an ugly switch in there (to call the correct TryParse) but I think you can achieve the desired functionality.

一旦您对类型有了更多的了解,您就可以尝试解析它。您可能需要一些丑陋的开关(以调用正确的TryParse),但我认为您可以实现所需的功能。

If you need me to explain any of the above further, then please ask :)

如果您需要我进一步解释上述任何内容,请询问:)

回答by Keith

Do you mean to do something like this:

你的意思是做这样的事情:

Class test<T>
{
     T method1(object Parameter1){

         if( Parameter1 is T ) 
         {
              T value = (T) Parameter1;
             //do something with value
             return value;
         }
         else
         {
             //Parameter1 is not a T
             return default(T); //or throw exception
         }
     }
}

Unfortunately you can't check for the TryParse pattern as it is static - which unfortunately means that it isn't particularly well suited to generics.

不幸的是,您无法检查 TryParse 模式,因为它是静态的——不幸的是,这意味着它不是特别适合泛型。

回答by Dan Herbert

The only way to do exactly what you're looking for would be to use reflection to check if the method exists for T.

完全按照您的要求进行操作的唯一方法是使用反射来检查 T 是否存在该方法。

Another option is to ensure that the object you send in is a convertible object by restraining the type to IConvertible (all primitive types implement IConvertible). This would allow you to convert your parameter to the given type very flexibly.

另一种选择是通过将类型限制为 IConvertible(所有基本类型都实现 IConvertible)来确保您发送的对象是可转换对象。这将允许您非常灵活地将参数转换为给定类型。

Class test<T>
{
    int method1(IConvertible Parameter1){

        IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));

        T temp = Parameter1.ToType(typeof(T), provider);
    }
}

You could also do a variation on this by using an 'object' type instead like you had originally.

您也可以像最初那样使用“对象”类型来对此进行变体。

Class test<T>
{
    int method1(object Parameter1){

        if(Parameter1 is IConvertible) {

            IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));

            T temp = Parameter1.ToType(typeof(T), provider);

        } else {
           // Do something else
        }
    }
}

回答by Weeble

Short answer, you can't.

简短的回答,你不能。

Long answer, you can cheat:

答案很长,你可以作弊:

public class Example
{
    internal static class Support
    {
        private delegate bool GenericParser<T>(string s, out T o);
        private static Dictionary<Type, object> parsers =
            MakeStandardParsers();
        private static Dictionary<Type, object> MakeStandardParsers()
        {
            Dictionary<Type, object> d = new Dictionary<Type, object>();
            // You need to add an entry for every type you want to cope with.
            d[typeof(int)] = new GenericParser<int>(int.TryParse);
            d[typeof(long)] = new GenericParser<long>(long.TryParse);
            d[typeof(float)] = new GenericParser<float>(float.TryParse);
            return d;
        }
        public static bool TryParse<T>(string s, out T result)
        {
            return ((GenericParser<T>)parsers[typeof(T)])(s, out result);
        }
    }
    public class Test<T>
    {
        public static T method1(string s)
        {
            T value;
            bool success = Support.TryParse(s, out value);
            return value;
        }
    }
    public static void Main()
    {
        Console.WriteLine(Test<int>.method1("23"));
        Console.WriteLine(Test<float>.method1("23.4"));
        Console.WriteLine(Test<long>.method1("99999999999999"));
        Console.ReadLine();
    }
}

I made a static dictionary holding a delegate for the TryParse method of every type I might want to use. I then wrote a generic method to look up the dictionary and pass on the call to the appropriate delegate. Since every delegate has a different type, I just store them as object references and cast them back to the appropriate generic type when I retrieve them. Note that for the sake of a simple example I have omitted error checking, such as to check whether we have an entry in the dictionary for the given type.

我制作了一个静态字典,其中包含我可能想要使用的每种类型的 TryParse 方法的委托。然后我编写了一个通用方法来查找字典并将调用传递给适当的委托。由于每个委托都有不同的类型,我只是将它们存储为对象引用,并在检索它们时将它们转换回适当的泛型类型。请注意,为了一个简单的例子,我省略了错误检查,例如检查我们在字典中是否有给定类型的条目。

回答by niklasfi

Ok guys: Thanks for all the fish. Now with your answers and my research (especially the article on limiting generic types to primitives) I will present you my solution.

好的伙计们:谢谢所有的鱼。现在有了你的答案和我的研究(特别是关于将泛型类型限制为基元的文章),我将向你展示我的解决方案。

Class a<T>{
    private void checkWetherTypeIsOK()
    {
        if (T is int || T is float //|| ... any other types you want to be allowed){
            return true;
        }
        else {
            throw new exception();
        }
    }
    public static a(){
        ccheckWetherTypeIsOK();
    }
}