代码之家  ›  专栏  ›  技术社区  ›  Salman A

如何通过泛型类型的特定实例重载C#方法

  •  1
  • Salman A  · 技术社区  · 15 年前

    从C++背景来看,我已经遇到了一个基于泛型类型的特定实例的重载问题。以下操作不起作用,因为只有一个 Foo<T> 类是一直生成的,所以在 Method ,即 this Foo<A> Foo<B> 正如我所希望的那样。在C++中,我习惯于将模板实例化为唯一类型。

    using System.Collections.Generic;
    class A
    {
        // Concrete class
    }
    
    class B
    {
        // Concrete class
    }
    
    class Bar
    {
        public void OverloadedMethod(Foo<A> a) {} // do some A related stuff
        public void OverloadedMethod(Foo<B> b) {} // do some B related stuff
        public void OverloadedMethod(OtherFoo of) {} // do some other stuff
    
         public void VisitFoo(FooBase fb) { fb.Method(this); }
    }
    
    abstract class FooBase
    {
        public abstract void Method(Bar b);
    }
    
    class Foo<T> : FooBase
    {
        // Class that deals with As and Bs in an identical fashion.
        public override void Method(Bar b)
        {
            // Doesn't compile here
            b.OverloadedMethod(this);
        }
    }
    
    class OtherFoo : FooBase
    {
        public override void Method(Bar b)
        {
            b.OverloadedMethod(this);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            List<FooBase> ListOfFoos = new List<FooBase>();
            ListOfFoos.Add(new OtherFoo());
            ListOfFoos.Add(new Foo<A>());
            ListOfFoos.Add(new Foo<B>());
    
            Bar b = new Bar();
            foreach (FooBase fb in ListOfFoos)
                b.VisitFoo(fb);
            // Hopefully call each of the Bar::Overloaded methods
        }
    }
    

    编辑: 希望这更清楚一点。

    5 回复  |  直到 15 年前
        1
  •  3
  •   Jon Skeet    15 年前

    我现在有了一段真正完整的代码来演示这个问题。OP注意:请在发布代码之前尝试编译代码。为了走到这一步,我必须做很多事情。让别人尽可能容易地帮助你是件好事。我还删除了一些无关的部分。OtherFoo在这里并不重要,FooBase也不重要。

    class A {}
    class B {}
    
    class Bar
    {
        public static void OverloadedMethod(Foo<A> a) { }
        public static void OverloadedMethod(Foo<B> b) { }
    }
    
    class Foo<T>
    {
        // Class that deals with As and Bs in an identical fashion.
        public void Method()
        {
            // Doesn't compile here
            Bar.OverloadedMethod(this);
        }
    }
    

    是的,这不可编译。你到底期望它做什么?请记住,重载解析是在以下时间执行的: 编译时 ,而不是执行时间。正如fallen888所说,您可以强制转换并调用适当的重载方法,但是您希望编译器选择两个重载中的哪一个?你想让它做什么 Foo<string> 而不是 Foo<A> Foo<B> ?

    这一切都证明了.NET泛型确实与C++模板有很大的不同,当然…

        2
  •  2
  •   user49572 user49572    15 年前

    我还没有尝试过,但似乎你应该能够通过做A&实现你想要的;B可访问(例如,使用非循环访问者模式)。

        3
  •  1
  •   user50879 user50879    15 年前

    这适用于静态情况。处理实例函数会有点复杂。这 post from Jon Skeet 可能提供一种合理的方法来处理实例方法。

    class Program
    {
        static void Main(string[] args)
        {
            var testA = new Foo<A>();
            testA.Method();
            var testB = new Foo<B>();
            testB.Method();
            Console.ReadLine();
            var testString = new Foo<string>(); //Fails
            testString.Method(); 
            Console.ReadLine();
        }
    }
    
    class A { }
    class B { }
    class Bar
    {
        public static void OverloadedMethod(Foo<A> a)
        {
            Console.WriteLine("A");
        }
        public static void OverloadedMethod(Foo<B> b)
        {
            Console.WriteLine("B");
        }
    }
    class Foo<T>
    {
        static Foo()
        {
            overloaded = (Action<Foo<T>>)Delegate.CreateDelegate(typeof(Action<Foo<T>>), typeof(Bar).GetMethod("OverloadedMethod", new Type[] { typeof(Foo<T>) }));
        }
    
        public void Method()
        {
            overloaded(this);
        }
    
        private static readonly Action<Foo<T>> overloaded;
    }
    
        4
  •  0
  •   BenAlabaster    15 年前

    编辑:我不确定您是否可以在尝试时完成此操作。我已经尝试了各种各样的技巧,试图让它工作,但无法让它编译。我能做的最好的事情就是将方法调用拉到泛型类之外。如果您的方法调用在外部,那么您可以具体定义泛型中的T。但是,在方法内部,在编译时,编译器不知道t是什么,因此不知道调用哪个重载方法。我能看到的唯一方法是使用开关来确定T的类型,并手动指定要调用的重载。

    class Stuff<T>
    {
        public T value { get; set; }
    }
    
    class Program
    {
        static void DummyFunc(Stuff<int> inst)
        {
            Console.WriteLine("Stuff<int>: {0}", inst.value.ToString());
        }
    
        static void DummyFunc(Stuff<string> inst)
        {
            Console.WriteLine("Stuff<string>: {0}", inst.value);
        }
        static void DummyFunc(int value)
        {
            Console.WriteLine("int: {0}", value.ToString());
        }
        static void DummyFunc(string value)
        {
            Console.WriteLine("string: {0}", value);
        }
        static void Main(string[] args)
        {
            var a = new Stuff<string>();
            a.value = "HelloWorld";
    
            var b = new Stuff<int>();
            b.value = 1;
    
            var c = "HelloWorld";
            var d = 1;
    
            DummyFunc(a);
            DummyFunc(b);
            DummyFunc(c);
            DummyFunc(d);
        }
    }
    

    并且得到了输出:

    Stuff<string>: HelloWorld
    Stuff<int>: 1
    string: HelloWorld
    int: 1
    

    我有四个重载函数引用了两个引用泛型类(一个用于int,一个用于string)和两个引用常规类型(一个用于int,一个用于string),所有这些都可以正常工作。。。这就是你想要的吗?

        5
  •  0
  •   Salman A    15 年前

    我希望找到一种更简单的方法来实现这一点,但现在我要做的是:

    代替 Foo<T>

    abstract class Foo<T> : FooBase
    {
        // Class that deals with As and Bs in an identical fashion.
    }
    
    class Foo_A : Foo<A>
    {
        public override void Method(Bar b)
        {
            b.OverloadedMethod(this);
        }
    }
    
    class Foo_B : Foo<B>
    {
        public override void Method(Bar b)
        {
            // Doesn't compile here
            b.OverloadedMethod(this);
        }
    }
    

    并将实例化更改为

    List<FooBase> ListOfFoos = new List<FooBase>();
    ListOfFoos.Add(new OtherFoo());
    ListOfFoos.Add(new Foo_A());
    ListOfFoos.Add(new Foo_B());
    

    这至少不需要在中重复应用代码 ,只要求我转发构造函数。