繁体   English   中英

重载泛型方法

[英]Overloading generic methods

当调用用于存储对象的泛型方法时,偶尔需要以不同方式处理特定类型。 我知道你不能基于约束重载,但任何其他替代方案似乎都存在自己的问题。

public bool Save<T>(T entity) where T : class
{ ... some storage logic ... }

我喜欢做的事情如下:

public bool Save<SpecificClass>(T entity)
{ ... special logic ... }

在过去,我们的团队已经创建了“一次性”方法来保存这些类,如下所示:

public bool SaveSpecificClass(SpecificClass sc)
{ ... special logic ... }

但是,如果您不知道该函数存在,并且您尝试使用泛型(保存),那么您可能遇到一系列问题,即“一次性”应该修复。 如果一个新的开发人员出现,看到通用的问题,并决定他将用他自己的一次性功能来解决它​​,这可能会变得更糟。

所以...

解决这个看似常见的问题有哪些选择?

我已经看过了,并且使用了UnitOfWork,现在它似乎是实际解决问题的唯一选择 - 但似乎是用大锤攻击苍蝇。

你可以这样做:

public bool Save<T>(T entity) where T : class
{ ... some storage logic ... }

public bool Save(SpecificClass entity)
{ ... special logic ... }

例如:

public class SpecificClass
{
}

public class Specializer
{
    public bool GenericCalled;
    public bool SpecializedCalled;

    public bool Save<T>(T entity) where T : class
    {
        GenericCalled = true;
        return true;
    }

    public bool Save(SpecificClass entity)
    {
        SpecializedCalled = true;
        return true;
    }
}

public class Tests
{
    [Test]
    public void TestSpecialization()
    {
        var x = new Specializer();
        x.Save(new SpecificClass());
        Assert.IsTrue(x.SpecializedCalled);
        Assert.IsFalse(x.GenericCalled);
    }
}

因为涉及泛型的函数和运算符重载在编译时绑定而不是在运行时绑定,如果代码有两个方法:

public bool Save<T>(T entity) ...
public bool Save(SomeClass entity) ...

然后尝试调用Save(Foo)代码,其中Foo是某种泛型类型的变量,总是会调用前一个重载,即使泛型类型恰好是SomeClass 我建议解决这个问题的方法是使用非泛型方法DoSave(T param)定义一个通用接口ISaver<in T> 让提供Save方法的类为它可以处理的类型实现所有适当的通用接口。 然后有对象的Save<T>方法尝试投this一个ISaver<T> 如果演员成功,请使用生成的ISaver<T> ; 否则执行一般保存。 如果类类型声明列出了它可以保存的类型的所有适当接口,则此方法将调用Save调用到正确的方法。

基本上C#不允许模板特化,除非通过这样的继承:

interface IFoo<T> { }
class Bar { }

class FooBar : IFoo<Bar> { }

至少它在编译期间不支持这一点。 但是,您可以使用RTTI来完成您要实现的目标:

public bool Save<T>(T entity)
{
    // Check if "entity" is of type "SpecificClass"
    if (entity is SpecificClass)
    {
        // Entity can be safely casted to "SpecificClass"
        return SaveSpecificClass((SpecificClass)entity);
    }

    // ... other cases ...
}

is表现是相当方便的做运行时类型检查。 它的工作原理类似于以下代码:

if (entity.GetType() == typeof(SpecificClass))
    // ...

编辑 :未知类型使用以下模式很常见:

if (entity is Foo)
    return DoSomethingWithFoo((Foo)entity);
else if (entity is Bar)
    return DoSomethingWithBar((Bar)entity);
else
    throw new NotSupportedException(
        String.Format("\"{0}\" is not a supported type for this method.", entity.GetType()));

编辑2 :正如其他答案建议使用SpecializedClass 重载方法,如果您正在使用多态,则需要注意。 如果您正在使用存储库的接口(实际上这是设计存储库模式的一种好方法),则有些情况下,重载将导致您调用错误的方法,无论您是否传递SpecializedClass的对象到界面:

interface IRepository
{
    bool Save<T>(T entity)
        where T : class;
}

class FooRepository : IRepository
{
    bool Save<T>(T entity)
    {
    }

    bool Save(Foo entity)
    {
    }
}

如果您直接使用Foo实例调用FooRepository.Save ,则此方法有效:

var repository = new FooRepository();
repository.Save(new Foo());

但是,如果您正在调用接口(例如,如果您使用模式来实现存储库创建),则这不起作用:

IRepository repository = GetRepository<FooRepository>();
repository.Save(new Foo());  // Attention! Call's FooRepository.Save<Foo>(Foo entity) instead of FooRepository.Save(Foo entity)!

使用RTTI只有一种Save方法,你会没事的。

为什么为您的方法使用不同的名称?

请参阅以下内容:

    public class Entity
    {
    }

    public class SpecificEntity : Entity
    {
    }

    public class Program
    {
        public static void Save<T>(T entity)
            where T : class
        {
            Console.WriteLine(entity.GetType().FullName);
        }

        public static void Save(SpecificEntity entity)
        {
            Console.WriteLine(entity.GetType().FullName);
        }

        private static void Main(string[] args)
        {
            Save(new Entity());          // ConsoleApplication13.Entity
            Save(new SpecificEntity());  // ConsoleApplication13.SpecificEntity

            Console.ReadKey();
        }
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM