【问题标题】:C# Generic and methodC# 泛型和方法
【发布时间】: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 一些装箱/拆箱参考。

标签: c# .net generics .net-2.0 c#-2.0


【解决方案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 类有很多私有方法,这些私有方法由执行方法调用,具体取决于对象类型。它需要重定向到好方法。
  • 考虑使用从类型到委托的映射来调用。见stackoverflow.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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-27
    • 2011-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多