C# 重载泛型方法

C# 重载泛型方法,c#,generics,overloading,C#,Generics,Overloading,调用用于存储对象的泛型方法时,有时需要以不同方式处理特定类型。我知道你不能基于约束而超载,但任何其他选择似乎都会带来自身的问题 public bool Save<T>(T entity) where T : class { ... some storage logic ... } 但是,如果您不知道该函数的存在,并且尝试使用泛型(Save),那么您可能会遇到许多“一次性”应该解决的问题。如果一个新的开发人员出现,发现泛型的问题,并决定用自己的一次性函数来修复它,情况可能会变得更糟

调用用于存储对象的泛型方法时,有时需要以不同方式处理特定类型。我知道你不能基于约束而超载,但任何其他选择似乎都会带来自身的问题

public bool Save<T>(T entity) where T : class
{ ... some storage logic ... }
但是,如果您不知道该函数的存在,并且尝试使用泛型(Save),那么您可能会遇到许多“一次性”应该解决的问题。如果一个新的开发人员出现,发现泛型的问题,并决定用自己的一次性函数来修复它,情况可能会变得更糟

所以

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

我已经研究并使用了UnitOfWork,而现在这似乎是真正解决问题的唯一选择——但似乎是用大锤攻击苍蝇。

你可以:

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

public bool Save(SpecificClass entity)
{ ... special logic ... }
public bool Save(T实体),其中T:class
{…某些存储逻辑…}
公共布尔保存(SpecificClass实体)
{…特殊逻辑…}
例如:

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);
    }
}
公共类特定类
{
}
公共类专门化器
{
公共布尔一般称为;
公共图书馆专业化;
公共布尔存储(T实体),其中T:class
{
GenericCalled=true;
返回true;
}
公共布尔保存(SpecificClass实体)
{
SpecializedCalled=true;
返回true;
}
}
公开课考试
{
[测试]
公共void TestSpecialization()
{
var x=新的专门化器();
x、 保存(新SpecificClass());
Assert.IsTrue(x.SpecializedCalled);
Assert.IsFalse(x.GenericCalled);
}
}

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

见下文:

public bool Save<SpecificClass>(T entity)
{ ... special logic ... }
    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();
        }
    }
公共类实体
{
}
公共类特定实体:实体
{
}
公共课程
{
公共静态无效保存(T实体)
T:在哪里上课
{
Console.WriteLine(entity.GetType().FullName);
}
公共静态作废保存(SpecificEntity)
{
Console.WriteLine(entity.GetType().FullName);
}
私有静态void Main(字符串[]args)
{
保存(新实体());//控制台应用程序13.Entity
保存(新SpecificEntity());//控制台应用程序13.SpecificEntity
Console.ReadKey();
}
}
基本上C#不允许模板专门化,除非通过如下继承:

interface IFoo<T> { }
class Bar { }

class FooBar : IFoo<Bar> { }
编辑:未知类型使用以下模式非常常见:

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重载方法,如果使用多态性,则需要小心。如果您正在为存储库使用接口(这实际上是设计存储库模式的一种好方法),那么在某些情况下,重载会导致调用错误的方法get,无论您是否将
SpecializedClass
的对象传递给接口:

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

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

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

IRepository repository=GetRepository();
repository.Save(新的Foo());//注意!调用的FooRepository.Save(Foo实体)而不是FooRepository.Save(Foo实体)!

使用RTTI只有一个
Save
方法,您就没事了。

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

public bool Save<T>(T entity) ...
public bool Save(SomeClass entity) ...
public bool Save(T实体)。。。
公共布尔保存(SomeClass实体)。。。

然后,尝试调用
Save(Foo)
的代码(其中
Foo
是某个泛型类型的变量)将始终调用前一个重载,即使泛型类型恰好是
SomeClass
。我的建议是使用非通用方法
DoSave(T param)
定义一个通用接口
ISaver
。让提供
Save
方法的类为它可以处理的类型实现所有适当的通用接口。然后让对象的
Save
方法尝试将
this
强制转换为
ISaver
。如果强制转换成功,则使用生成的
ISaver
;否则,执行常规保存。如果类类型声明为其保存的类型列出了所有适当的接口,那么该方法将发送“代码>保存/代码>调用适当的方法。

不同于C++,C.*不允许模板专用化:这些SAVER()方法放置在一些轻量级的助手类或一些实体类中吗?我只是在考虑继承,但重要的是要确保这是正确的方式,因为继承并不总是被正确使用。可能重复使用特定类型(如
Save(SpecificClass entity)
的重载有什么问题,它没有什么问题,但它仍然是一个重载,而不是一个专门化问题是关于“超载”…)没有人说过不同的话。。。我只是回答OP的评论:“但是,如果您不知道该函数存在,并且尝试使用泛型(Save)然后,您可能会遇到许多“一次性”应该解决的问题。如果新开发人员出现,看到泛型的问题,并决定使用自己的一次性功能来解决,情况可能会变得更糟。”。而且它也有效。这只是已经提供的答案的一个替代方案。不需要否决它……我也更喜欢使用RTTI,因为它使开发人员更容易正确地执行它(只需要调用一个方法),而不是错误地执行它(可能调用错误的方法)。还要看一下问题注释,因为专门化是OP所做的(不管他是否要求“重载”)。我无法让它工作:调用了函数的通用版本
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()));
interface IRepository
{
    bool Save<T>(T entity)
        where T : class;
}

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

    bool Save(Foo entity)
    {
    }
}
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)!
public bool Save<T>(T entity) ...
public bool Save(SomeClass entity) ...