c# 中的泛型 & 访问 T 的静态成员

Posted

技术标签:

【中文标题】c# 中的泛型 & 访问 T 的静态成员【英文标题】:Generics in c# & accessing the static members of T 【发布时间】:2010-09-06 09:33:27 【问题描述】:

我的问题涉及 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()
     

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


编辑:谢谢大家的回答!

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

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


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

【问题讨论】:

【参考方案1】:

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

Constraints on Type Parameters

【讨论】:

【参考方案2】:

简短的回答,你不能。

长答案,你可以作弊:

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();
    

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

【讨论】:

【参考方案3】:

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

在上面的实例中 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;
       
    

【讨论】:

【参考方案4】:

另一种方法,这次是混合中的一些反思:

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;
    

下一步将尝试实施

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

具有完整的参数类型匹配等。

【讨论】:

这太慢了。您应该缓存一个通用委托。【参考方案5】:

这并不是一个真正的解决方案,但在某些情况下它可能是一个不错的选择:我们可以将一个额外的委托传递给泛型方法。

为了澄清我的意思,让我们举个例子。假设我们有一些通用工厂方法,它应该创建一个 T 的实例,然后我们希望它调用另一个方法,用于通知或额外的初始化。

考虑以下简单类:

public class Example

    // ...

    public static void PostInitCallback(Example example)
    
        // Do something with the object...
    

还有下面的静态方法:

public static T CreateAndInit<T>() where T : new()

    var t = new T();
    // Some initialization code...
    return t;

所以现在我们必须这样做:

var example = CreateAndInit<Example>();
Example.PostInitCallback(example);

然而,我们可以改变我们的方法来接受一个额外的委托:

public delegate void PostInitCallback<T>(T t);
public static T CreateAndInit<T>(PostInitCallback<T> callback) where T : new()

    var t = new T();
    // Some initialization code...
    callback(t);
    return t;

现在我们可以将调用更改为:

var example = CreateAndInit<Example>(Example.PostInitCallback);

显然,这仅在非常特定的情况下才有用。但这是最干净的解决方案,因为我们获得了编译时安全,不涉及“黑客”,而且代码非常简单。

【讨论】:

【参考方案6】:

你的意思是这样的:

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
         
     

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

【讨论】:

【参考方案7】:

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

另一种选择是通过将类型限制为 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);
    

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

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
        
    

【讨论】:

【参考方案8】:

好的,伙计们:谢谢所有的鱼。现在有了你的答案和我的研究(尤其是limiting generic types to primitives 上的文章),我将向你展示我的解决方案。

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();
    

【讨论】:

【参考方案9】:

你可能做不到。

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

【讨论】:

【参考方案10】:

您可能想阅读我之前在limiting generic types to primitives 上的帖子。这可能会在限制可以传递给泛型的类型方面为您提供一些指示(因为 TypeParse 显然仅适用于一组基元( string.TryParse 显然是例外,这是没有意义的)。

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

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

【讨论】:

【参考方案11】:

最佳代码:以这种方式将 T 限制为 ValueType:

class test1<T> where T: struct

这里的“结构”表示值类型。 String 是一个类,而不是值类型。 int、float、Enums都是值类型。

顺便说一句,编译器不接受调用静态方法或访问“类型参数”上的静态成员,如以下示例中将无法编译的:(

class MyStatic  public static int MyValue=0; 
class Test<T> where T: MyStatic

    public void TheTest()  T.MyValue++; 

=> 错误 1“T”是一个“类型参数”,在给定的上下文中无效

SL。

【讨论】:

【参考方案12】:

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

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()

【讨论】:

以上是关于c# 中的泛型 & 访问 T 的静态成员的主要内容,如果未能解决你的问题,请参考以下文章

C#中的泛型是啥意思?

C#中的泛型

转载:C#中的泛型

在 C# XML 文档中引用泛型类型的泛型类型?

C#中的泛型详解

第2张 C#中的泛型