C#:泛型,多态和专业化

时间:2012-02-28 17:37:53

标签: c# generics specialization

我正在尝试使用具有专业化的泛型。请参阅下面的代码。我想要做的是让运行时引擎理解该函数的特化是基于类型可用的,它应该使用它而不是泛型方法。是否可以不使用关键字动态

public interface IUnknown 
{
    void PrintName<T>(T someT);
}

public interface IUnknown<DerivedT> : IUnknown
{
    //***** I am trying to make runtime engine understand that method below is 
    //***** specialization of void PrintName<T>(T someT);
    void PrintName(DerivedT derivedT);
}

public class SoAndSo<DerivedT> : IUnknown<DerivedT>
{
    public void PrintName<T>(T someT) { Console.WriteLine("PrintName<T>(T someT)"); }
    public void PrintName(DerivedT derivedT) { Console.WriteLine("PrintName(DerivedT derivedT)"); }
}

public class Test
{
    public static void TestIt()
    {
        List<IUnknown> unknowns = new List<IUnknown>();
        unknowns.Add(new SoAndSo<int>());
        unknowns.Add(new SoAndSo<string>());

        //*** statement below should print "PrintName(DerivedT derivedT)"
        unknowns[0].PrintName(10);
        //*** statement below should print "PrintName<T>(T someT)"
        unknowns[0].PrintName("abc");


        //********** code snippet below works exactly as expected ************
        dynamic d;
        d = unknowns[0];
        d.PrintName(10);    // <=== prints "PrintName(DerivedT derivedT)"
        d.PrintName("abc"); // <=== prints "PrintName<T>(T someT)"
    }

}

修改

如果没有任何方法可以实现我想要的而不使用关键字动态,有没有任何优雅的方法来实现转换到具体类型而没有巨大的枚举\ flag \ switch-case?

编辑 - 可能实现这一目标的一种方式 我想发布这个作为答案,但这不是真正基于多态或重载,所以决定把它作为编辑。如果这是有道理的,请告诉我。

public abstract class IUnknown
{
    public abstract void PrintName<T>(T someT);
}


public abstract class IUnknown<DerivedT /*, DerivedType*/> : IUnknown //where DerivedType : IUnknown<DerivedT, DerivedType>
{
    MethodInfo _method = null;

    //***** I am trying to make runtime engine understand that method below is 
    //***** specialization of void PrintName<T>(T someT);
    public override sealed void PrintName<T>(T derivedT)
    {
        bool isSameType = typeof(T) == typeof(DerivedT);
        if (isSameType && null == _method)
        {

            //str = typeof(DerivedT).FullName;
            Type t = GetType();

            _method = t.GetMethod("PrintName", BindingFlags.Public |
                           BindingFlags.Instance,
                           null,
                           CallingConventions.Any,
                           new Type[] { typeof(T) },
                           null);


        } 

        if (isSameType && null != _method)
        {
            _method.Invoke(this, new object[] { derivedT });
        }
        else
        {
            PrintNameT(derivedT);
        }

    }

    public virtual void PrintNameT<T>(T derivedT)
    {
    }

    public virtual void PrintName(DerivedT derivedT) { Console.WriteLine("PrintName(DerivedT derivedT)"); }

    //public static DerivedType _unknownDerivedInstance = default(DerivedType);

}

public class SoAndSo<DerivedT> : IUnknown<DerivedT> //, SoAndSo<DerivedT>>
{
    //static SoAndSo() { _unknownDerivedInstance = new SoAndSo<DerivedT>(); }
    public override void PrintNameT<T>(T someT) {  /*Console.WriteLine("PrintNameT<T>(T someT)");*/ }

    public override void PrintName(DerivedT derivedT) { /*Console.WriteLine("PrintName(DerivedT derivedT)");*/ }
}


public static class Test
{

    public static void TestIt()
    {
        List<IUnknown> unknowns = new List<IUnknown>();
        unknowns.Add(new SoAndSo<int>());
        unknowns.Add(new SoAndSo<float>());


         //*** statement below should print "PrintName(DerivedT derivedT)"
        unknowns[0].PrintName(10);
        //*** statement below should print "PrintName<T>(T someT)"
        unknowns[0].PrintName(10.3);



        //*** statement below should print "PrintName(DerivedT derivedT)"
        unknowns[1].PrintName(10);
        //*** statement below should print "PrintName<T>(T someT)"
        unknowns[1].PrintName(10.3f);


        System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
        stopWatch.Start();
        for (int i = 0; i < 1000000; ++i)
        {
            unknowns[0].PrintName(10.3);
        }
        stopWatch.Stop();

        System.Diagnostics.Trace.TraceInformation("Milliseconds: {0}", stopWatch.ElapsedMilliseconds);


        //********** code snippet below works exactly as expected ************
        dynamic d;
        d = unknowns[0];
        d.PrintName(10);    // <=== prints "PrintName(DerivedT derivedT)"
        d.PrintName("abc"); // <=== prints "PrintName<T>(T someT)"
    }

提前致谢, -Neel。

3 个答案:

答案 0 :(得分:3)

我不相信有任何办法可以做到这一点。它根本不是CLR支持的执行时调度机制的一部分。你当然可以写这个:

public void PrintName<T>(T someT)
{
    // This is assuming you want it based on the type of T,
    // not the type of the value of someT
    if (typeof(DerivedT).IsAssignableFrom(typeof(T))
    {
        PrintName((DerivedT)(object) someT);
        return;
    }
    Console.WriteLine("PrintName<T>(T someT)"); 
}

......但这并不是非常令人愉快。

答案 1 :(得分:0)

您可以通过明确实施IUnknown<DerivedT>来实现这一目标。但是,我不确定这是你在找什么。

public class SoAndSo<DerivedT> : IUnknown<DerivedT>
{
    public void PrintName<T>(T someT) { Console.WriteLine("PrintName<T>(T someT)"); }
    void IUnknown<DerivedT>.PrintName(DerivedT derivedT) { Console.WriteLine("PrintName(DerivedT derivedT)"); }
}

public class Test
{
    public static void TestIt()
    {
        List<IUnknown> unknowns = new List<IUnknown>();
        unknowns.Add(new SoAndSo<int>());
        unknowns.Add(new SoAndSo<string>());

        //*** statement below should print "PrintName(DerivedT derivedT)"
        (unknowns[0] as IUnknown<int>).PrintName(10);
        //*** statement below should print "PrintName<T>(T someT)"
        unknowns[0].PrintName("abc");
    }
}

答案 2 :(得分:0)

我建议定义一个通用静态类NamePrinter<T>,其中Action<T>名为PrintName,它最初指向一个私有方法,用于检查T是否为特殊类型并将PrintName设置为专用版本或非专用版本(如果需要,非专用版本可能会抛出异常),然后调用PrintName委托。如果有人这样做,那么第一次为任何特定NamePrinter<T>.PrintName(T param)调用T时,代码必须检查类型T以确定要使用哪种“真实”方法,但将来的调用将是直接派遣到适当的日常工作。