使用约束调用通用扩展方法

时间:2015-06-29 15:26:48

标签: c# generics

我有一个包含两种不同类型属性的模型类 - SourceDataDestinationData。这些属性包含类似类的实例(在不同的命名空间中),我需要手动比较并显示差异。

我想为这个问题创建通用扩展方法IsSame,所以我可以简单地写一下:

// SourceData: x => ..., DestinatinData: y => ...
var nameIsSame = model.IsSame(x => x.Name, y => y.Name);
var ageIsSame = model.IsSame(x => x.Age, y => y.Age);
...

我准备了这个场景的样本,但我不能编写可以像上面那样调用的方法IsSame。我必须为泛型方法调用指定所有类型。

public interface ISource 
{ }

public interface IDestination 
{ }

public class SourcePerson : ISource 
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class DestinationPerson : IDestination 
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public abstract class DetailViewModel<TSource, TDestination> 
    where TSource : ISource
    where TDestination : IDestination
{
    public TSource SourceData { get; set; }
    public TDestination DestinationData { get; set; }
}

public class PersonDetailViewModel : DetailViewModel<SourcePerson, DestinationPerson>
{ }

现在我有类型PersonDetailViewModel的模型类,它实现了抽象类DetailViewModel<>。我认为应该简单地通过提供可能因约束而被识别的ExtensionMethods.IsSame实例来调用方法PersonDetailViewModel的实现。

public static class ExtensionMethods 
{
    public static bool IsSame<TModel, TSource, TDestination, TValue>(this TModel model, Func<TSource, TValue> sourceProperty, Func<TDestination, TValue> destinationProperty)
        where TModel : DetailViewModel<TSource, TDestination> 
        where TSource : ISource
        where TDestination : IDestination
    {
        if (model.SourceData == null) {
            return (model.DestinationData == null);
        }

        if (model.DestinationData == null) 
            return false;

        return Equals(sourceProperty(model.SourceData), destinationProperty(model.DestinationData));
    }
}

然而,当我尝试调用扩展方法时,我必须指定所有类型,即使编译器应该知道所有类型。

class Program
{
    static void Main(string[] args)
    {
        var model = new PersonDetailViewModel
        {
            SourceData = new SourcePerson { Name = "Karel Gott", Age = 72 },
            DestinationData = new DestinationPerson { Name = "Karel Engles", Age = 72 }
        };

        Console.WriteLine(model);
        Console.WriteLine();

        //var nameIsSame = model.IsSame(x => x.Name, y => y.Name); // doesn't work :-(
        var nameIsSame = model.IsSame<PersonDetailViewModel, SourcePerson, DestinationPerson, string>(x => x.Name, y => y.Name);
        Console.WriteLine("Name is same: " + nameIsSame);

        //var ageIsSame = model.IsSame(x => x.Age, y => y.Age); // doesn't work :-(
        var ageIsSame = model.IsSame<PersonDetailViewModel, SourcePerson, DestinationPerson, int>(x => x.Age, y => y.Age);
        Console.WriteLine("Age is same: " + ageIsSame);

        Console.WriteLine();
        Console.WriteLine("Press any key to exit ...");
        Console.ReadKey(true);
    }
}

关于所有人的通知:请不要写信给我,我应该区别对待。我在这里使用方法IsSame只是为了简单。该方法运行良好,内部代码应该做其他事情。我只需要在没有明确定义类型的情况下调用它。在我看来,编译器应该从约束中了解它们。

在这种情况下,我使用了类PersonDetailViewModel,SourcePerson,DestinationPerson ......但在我的应用程序中有很多这样的类。 TSource和TDestination里面没有相同的代码,属性应该有不同的名称。想象一下,我想比较这些类中的属性:

public class SourceCompany : ISource {
  public int CompanyId { get; set; }
  public string NameTradeRegister { get; set; }
  public string AddressStreet { get; set; }
  public string Town { get; set; }
}

public class DestinationCompany : IDestination {
  public int ID { get; set; }
  public string Name { get; set; }
  public string StreetName { get; set; }
  public string City { get; set; }
}

var idIsSame = model.IsSame(x => CompanyId, y => y.ID);

好的,我找到了解决方案。扩展方法应该避免使用TModel,而是可以声明DetailViewModel。

public static class ExtensionMethods 
{
    public static bool IsSame<TSource, TDestination, TValue>(this DetailViewModel<TSource, TDestination> model, Func<TSource, TValue> sourceProperty, Func<TDestination, TValue> destinationProperty)
       where TSource : ISource
       where TDestination : IDestination
    {
        if (model.SourceData == null) {
            return (model.DestinationData == null);
        }

        if (model.DestinationData == null)
            return false;

        return Equals(sourceProperty(model.SourceData), destinationProperty(model.DestinationData));
    }
}

0 个答案:

没有答案