首页 > 解决方案 > 是否可以让通用子类在 c# 中看到相同变量的不同接口

问题描述

让我描述一个简化的故事。我尝试设计一个推荐功能,它会通过不同的匹配规则为你推荐不同的产品。

// TV will match a product by given TV related parameters

public class TvMatcher
{
    public Product Match(ITvMatchParam tvMatchParam)
    {
        return new Product(); // pick a tv by tvMatchParam
    }
}

public interface ITvMatchParam
{
    int Age { get; }
    int Gender { get; }
}
// Phone will match a product by given Phone related parameters

public class PhoneMatcher
{
    Product Match(IPhoneMatchParam phoneMatchParam)
    {
        return new Product(); // pick a phone by phoneMatchParam
    }
}

public interface IPhoneMatchParam
{
    int Age { get; }
    int Education { get; }
}

Recommend是一个接受联合参数的公共 API。然后它会选择一个 Matcher 并调用 match() 来选择一个产品。

public class Main
{
    public Product Recommend(AllMatchParams allParams)
    {
        // pseudo code here, can't compile
        IMatcher matcher = CreateMatcherFactoryMethod(allParams);
        return matcher.Match(allParams);
    }

    private static IMatcher CreateMatcherFactoryMethod(AllMatchParams allParams)
    {
        IMatcher matcher;
        if (allParams.Product == 0)
        {
            matcher = new TvMatcher();
        }
        else
        {
            matcher = new PhoneMatcher();
        }

        return matcher;
    }
}

class AllMatchParams : ITvMatchParam, IPhoneMatchParam
{
    public int Age { get; }
    public int Education { get; }
    public int Gender { get; }
    public int Product { get; }
}

但是上面的代码不会被编译,因为 TvMatcher 和 PhoneMatcher 不能实现 IMatcher

public interface IMatcher
{
    Product Match(AllMatchParams allMatchParams);
}

由于TvMatcher只关心Tv相关参数,PhoneMatcher只关心PhoneMatcher相关参数,我不想强​​制PhoneMatcher实现IMatcher。

public class PhoneMatcher: IMatcher
{
    Product Match(AllMatchParams allParams)
    {
        // don't like this because PhoneMatcher only cares subset properties in allParams
    }
}

所以我尝试实现一个 AbstractClass 并让 PhoneMatcher 继承它。我希望它可以接受 AllMatchParams 并且只在实现中看到 PhoneMatchParam。

public abstract class AbstractMatcher<TProductMatchParam>: IMatcher
{
    public Product Match(AllMatchParams allMatchParams)
    {
        return ProductMatch(allMatchParams);  // fails here
    }

    protected abstract Product ProductMatch(TProductMatchParam param);
}

public class PhoneMatcher: AbstractMatcher<IPhoneMatchParam>
{
    protected Product ProductMatch(IPhoneMatchParam phoneMatchParam)
    {
        // ...
    }
}
public class TvMatcher: AbstractMatcher<ITvMatchParam>
{
    protected Product ProductMatch(ITvMatchParam tvMatchParam)
    {
        // ...
    }
}

但是上面的代码不会编译,因为 AllMatchParams 不能转换为泛型变量<TProductMatchParam>

有没有办法让 AbstractMatcher.Match 可以接受一个固定类型的参数并委托给孩子,所以孩子可以看到该参数的不同接口?

我曾尝试向泛型变量添加约束,但失败了,因为无法同时IPhoneMatchParamITvMatchParam同时对 TProductMatchParam 进行约束。

标签: c#genericsinheritance

解决方案


AbstractMatcher的有点过分了

您可以IMatcher改为使您的界面通用:

public interface IMatcher<T>
{
    Product Match(T matchParam);
}

public class PhoneMatcher: IMatcher<IPhoneMatchParam>
{
    public Product Match(IPhoneMatchParam matchParam)
    {
        // ...
    }
}

public class TvMatcher: IMatcher<ITvMatchParam>
{
    public Product Match(ITvMatchParam matchParam)
    {
        // ...
    }
}

在这种情况下,您可以通过AllMatchParams2 种方法传递您的实例。

return new PhoneMatcher().Match(criteria) ?? new TvMatcher().Match(criteria);

将返回第一个非空匹配产品


推荐阅读