C# 泛型和方法

Posted

技术标签:

【中文标题】C# 泛型和方法【英文标题】:C# Generic and method 【发布时间】:2008-11-18 20:16:57 【问题描述】:

我怎样才能选择好的方法(我在下面的示例中显示了 2 种不同的方法不起作用)。我使用而不是带有 IF 和 IS 的 Object 类型的变量来完成这项工作,但我试图避免使用 Object 和装箱/拆箱。所以我认为 Generic 可以完成这项工作,但我被困在这里。

这是说明我的问题的一小段代码:

class Program

    static void Main(string[] args)
    
        Parser p = new Parser();
        ObjectType1 o1 = new ObjectType1();
        p.execute(o1);
        Console.Read();
    


class Parser

    public T execute<T>(T obj)
    
        /*
        if (obj is ObjectType1)
            this.action((ObjectType1)obj);
        else if (obj is ObjectType2)
            this.action((ObjectType2)obj);
        */
        this.action(obj);
        return obj;
    

    private void action(ObjectType1 objectType1)
    
        Console.WriteLine("1");
    

    private void action(ObjectType2 objectType2)
    
        Console.WriteLine("2");
    



class ObjectType1



class ObjectType2


更新

我不想要接口和类。对不起。我知道这不是问题的目标。

使用 (ObjectType)obj 进行投射不起作用,但如果你这样做:

        if (obj is ObjectType1)
            this.action(obj as ObjectType1);
        else if (obj is ObjectType2)
            this.action(obj as ObjectType1);

它有效...为什么?

而且...我不能为所有类型重载执行方法,因为此方法来自接口。这就是为什么所有都需要从这个方法中调用。

【问题讨论】:

对象没有装箱和拆箱——它不是一个值类型。我只是删除泛型或重载方法 对象到真实类型......在将真实类型操作到对象之后...... 无论如何都没有拳击。这里没有值类型,因此没有装箱。 它是一个真实的对象--->以对象类型操作数据的解析器---->返回真实的对象。我看到一些从真实对象到对象的转换,而不是从对象到真实对象到过程的转换(对于这个问题,代码有点 sn-p)。所以是的,它确实有一些装箱/拆箱。 csharphelp.com/archives/archive100.html 一些装箱/拆箱参考。 【参考方案1】:

不,你不能这样做。泛型不像 C++ 模板那样工作——泛型方法只编译一次。编译器可以用于重载解析的唯一信息是它在泛型方法中知道的信息,无论使用什么代码。

作为一个示例来说明这一点,这里有一段代码可能无法按照您的预期工作:

using System;

class Test
    
    static void Main()
    
        string x = "hello";
        string y = string.Copy(x);

        Console.WriteLine(x==y); // Overload used
        Compare(x, y);
    

    static void Compare<T>(T x, T y) where T : class
    
        Console.WriteLine(x == y); // Reference comparison
    

如果不进一步了解您想要做什么,很难说出最佳的方法。

【讨论】:

这个例子以很小的方式展示了我尝试做的事情。 Parser 类有很多私有方法,这些私有方法由执行方法调用,具体取决于对象类型。它需要重定向到好方法。 考虑使用从类型到委托的映射来调用。见***.com/questions/298976/… 这更多的是我搜索的我想,你如何设置地图?带有 的字典? Dictionary 这样你就可以调用动作了。 Action 不返回值,我的方法需要返回值。您如何使用该解决方案创建一个新帖子。这是我的尝试: Dictionary map = new Dictionary(); public Parser() map.Add(typeof(ObjectType1), new delMapper(action)); 【参考方案2】:

你考虑过接口吗?

interface IAction

   void action();


class ObjectType1 : IAction

   void action() 
      Console.WriteLine("1");
   


class ObjectType2 : IAction

    void action() 
      Console.WriteLine("2");
    


class Parser

   public IAction execute(IAction obj)
   
      obj.action();
      return obj;
   

由 OP 编辑​​:

此解决方案需要更改所有业务逻辑对象以具有此接口。这真的不是一件事情(在我的情况下)。而且,在其他情况下,我总是更喜欢干净的 BusinessObject 没有与业务无关的接口。在我的问题中,我想要一个与 Generic/Object/Delegate 方法更相关的解决方案来实现它。谢谢你。此答案将不被接受。

【讨论】:

是的,但我不能为此做 100 课......我必须告诉你,我们这里有很多方法,因为它是数据库对象构建器。是的,我考虑过,但我想将所有数据库对象构建在同一个类中 而且我不会将所有的 BusinessLogic 都更改为 IAction ...这个选项确实不适合这种情况。 很公平......不过,这听起来像是一个界面的工作。如果您不考虑接口的唯一原因是您有很多方法和类要修改,那么您可以编写一个脚本来为您自动化该过程:) 该类需要完成其工作而不是加载它或其他任务(这样,如果我们更改加载方式或其他方法,业务对象不会受到影响。我不相信在这种情况下需要一个接口,但需要很多(您的代码的方式非常适合其他目的)【参考方案3】:

类 Parser 有很多私有方法,根据对象类型由 execute 方法调用。它需要重定向到好方法。

编译器将为您完成这项工作。只需使用重载。

class Parser

    public ObjectType1 action(ObjectType1 objectType1)
    
        Console.WriteLine("1");
        return objectType1;
    
    public ObjectType2 action(ObjectType2 objectType2)
    
        Console.WriteLine("2");
        return objectType2;
    


class ObjectType1  
struct ObjectType2  

然后,调用:

Parser p = new Parser();
p.action(new ObjectType1());
p.action(new ObjectType2());

没有装箱/拆箱,并且会调用适当的方法。

【讨论】:

不错的答案,我会投票给你,但问题是实际上的方法来自一个接口,我不能不改变那个接口来为所有类型重载它。【参考方案4】:

我没试过,但是你能做到吗?

罢工>

public T execute<T>(T obj)

    this.action((T)obj);
    return obj;

(根据cmets,不行)

public T execute<T>(T obj)

    this.action(obj as T);
    return obj;

(根据cmets,作品)

【讨论】:

没必要,“obj”已经是T类型了,不需要强制转换。 如果“as”遵循真实类型而不是 T(哎呀,我没有很好地阅读我认为的解决方案),则第二个工作。所以它有效,但我仍然需要做很多 IF...【参考方案5】:

我知道您担心装箱/拆箱,所以这里可能涉及到 ValueTypes。

public T execute<T>(T obj)   
        
    this.action(obj);
    return obj;

假设该操作正在修改 obj,并且还假设 该修改对调用者很重要(这就是您将值返回给调用者的原因)。此代码存在令人讨厌的按值传递缺陷。

考虑这段代码:

    public int execute(int obj)   
            
        this.action(obj);
        return obj;
    

    public void action(int obj)
    
        obj = obj + 1;
    

以这种方式调用。

int x = p.execute(1);

x 是 1,而不是 2。

【讨论】:

代码是这样的,因为有时我们从缓存中获取数据(反序列化)并且我们有一个新的引用,这就是我们需要返回的原因。我们需要通过参数传递对象来知道要加载哪个对象……你明白了吗? 不管数据来自缓存还是蒙古,如果任何类型都是值类型,你就会遇到传值问题。【参考方案6】:

泛型发生在编译时。当您希望将相同的代码应用于不同的类型时,最好使用它。它不是动态的,因此无法帮助您根据输入类型在方法之间切换。

David B 的回复中的重载解析有效,但也发生在编译期间。

更新中的代码执行相同的操作。它强制转换(在仔细检查类型之后),然后使用重载来解析方法。

感觉你想根据运行时输入切换方法。

如果您使用反射,您可以获得更动态的行为。

        public object execute(object obj) 
        
            MethodInfo m = typeof(Parser).GetMethod(
                "action", 
                BindingFlags.Instance | BindingFlags.NonPublic, 
                null, 
                new Type[]  obj.GetType() , 
                null);
            m.Invoke(this, new object[]  obj );
            return obj; 
         

它可能有点脆弱,但它在示例中有效。

【讨论】:

【参考方案7】:

IIRC 您可以使用“where”子句来允许这样做

public T execute<T>(T obj) where : /* somthing */


我总是要自己用 Google 搜索那个,所以我会留在那里。

编辑:阅读一些 cmets。我不建议调用类型特定的代码。而是将该代码放在一个虚函数中并调用它。调用签名可能会很长,但这就是自动完成的目的。

Koodos to joshua.ewer for finding the man page

【讨论】:

如果有人在谷歌上搜索信息,这些被称为约束。可以基于类型、继承或实现等进行约束msdn.microsoft.com/en-us/library/d5x73970.aspx

以上是关于C# 泛型和方法的主要内容,如果未能解决你的问题,请参考以下文章

C#中的泛型和泛型集合

C#高级编程笔记 Day 5, 9月 13日 (泛型)

C#语言中泛型和委托的关系是啥,Func<int>是泛型还是委托?

Java:Effective java学习笔记之 优先考虑泛型和泛型方法

Java:Effective java学习笔记之 优先考虑泛型和泛型方法

Java 泛型和静态工厂方法——语法