Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/274.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#通用和专用扩展方法组合歧义_C#_Generics_Extension Methods_Type Parameter - Fatal编程技术网

C#通用和专用扩展方法组合歧义

C#通用和专用扩展方法组合歧义,c#,generics,extension-methods,type-parameter,C#,Generics,Extension Methods,Type Parameter,我有一个名为水果的抽象类。 然后我有一个名为Apple的派生类 我有以下两种扩展方法: public static IQueryable<TFruit> WithEagerLoading<TFruit>(this IQueryable<TFruit> query) where TFruit : Fruit { return query.EagerLoad(x => x.Distributors); // Fruit.Distributors }

我有一个名为水果的抽象类。 然后我有一个名为Apple的派生类

我有以下两种扩展方法:

public static IQueryable<TFruit> WithEagerLoading<TFruit>(this IQueryable<TFruit> query) where TFruit : Fruit
{
   return query.EagerLoad(x => x.Distributors); // Fruit.Distributors
}

public static IQueryable<Apple> WithEagerLoading(this IQueryable<Apple> query)
{
   query = query.EagerLoad(x => x.AppleBrands); // Apple.AppleBrands

   // now bubble up to base extension method
   return query.WithEagerLoading<Apple>();
}
这可不好——考虑到我有大约20个派生类型


有人能为我的尝试提供一个替代方案吗?

扩展方法解析在编译时进行。查询变量的类型为
IQueryable
,因此编译器会选择与
匹配的最具体的方法。它不能摘苹果,因为它只知道水果是某种水果。它只会选一次,而且是永久性的

您的建议要求它根据运行时的类型动态决定使用哪个扩展方法,或者编译独立版本的
IQueryable
,根据
TFruit
的特定值以不同方式解析方法

编辑以回答其他问题

这个特殊的外壳并不可怕,因为你可以使用switch语句。但我同意,如果你有很多类型,那就不太理想了。在授权给子类方面,我会稍微调整Paul的回答:

abstract class FruitRepository : IRepository<T> where TFruit : Fruit
{
   public TFruit FindByID(int fruitID)
   {
       //query stuff here

       query = AddEagerLoading(query)
                  .WithEagerLoading();
    }

    //this could also be abstract to prevent you from forgetting
    public virtual IQueryable<TFruit> AddEagerLoading(IQueryable<TFruit> query)
    {
        return query;
    }
}
抽象类FruitRepository:IRepository,其中TFruit:Fruit
{
公共TFruit FindByID(int FROUTID)
{
//在这里查询资料
查询=加载(查询)
.使用加载();
}
//这也可以是抽象的,以防止您忘记
公共虚拟IQueryable加载(IQueryable查询)
{
返回查询;
}
}
然后

class AppleRepository : FruitRepository<Apple>
{
    public override AddEagerLoading(IQueryable<Apple> query)
    {
        return query.EagerLoad(x => x.AppleBrands);
    }
}
class AppleRepository:存储库
{
公共覆盖加载(IQueryable查询)
{
返回query.eangerload(x=>x.AppleBrands);
}
}

这样,每个子类的代码就最少了。

最终,你需要找到一种引入多态性的方法——你想要加载苹果的特殊行为,它扩展了加载水果的基本行为。有时最简单的方法是创建存储库类,例如:

class Repository<T> : IRepository<T>
{

   public virtual T FindById(int id)
   { ... }
}

class FruitRepository<T> : Repository<T> where T : Fruit
{
   public override T FindById(int id)
   {  ... }
}

class AppleRepository : FruitRepository<Apple>
{
   public override T FindById(int id)
   {  ... }
}
类存储库:IRepository
{
公共虚拟T FindById(int-id)
{ ... }
}
类FruitRepository:Repository,其中T:Fruit
{
公共覆盖T FindById(int id)
{  ... }
}
类AppleRepository:FructureRepository
{
公共覆盖T FindById(int id)
{  ... }
}
现在FindByID不需要在方法级别使用泛型参数,它只在类级别使用泛型参数。然后,您可以根据需要找到相应的FruitRepository和AppleRepository override。您的使用代码可能有点不同,因为您必须确保您拥有的存储库实例适合查找苹果

如果您使用的是IoC容器,您可能会注册,当某个对象请求
IRepository
时,它会返回一个AppleRepository实例。那么你可以这样使用它:

// ideally you would resolve this via constructor injection, but whatever.
var repository = container.Resolve<IRepository<Apple>>(); 
var apple = repository.FindByID(1);
//理想情况下,您可以通过构造函数注入来解决这个问题,但不管怎样。
var repository=container.Resolve();
var apple=repository.FindByID(1);

如果您没有使用IoC容器。。。好。。您应该是:)

我遇到了一个类似的问题,我想有效地更改方法的优先级,以便它首先解决“专用”版本

您可以在不更改调用代码的情况下实现这一点,但该解决方案可能并不流行,因为它使用运行时反射和代码生成。不管怎样,我还是要把它扔出去(我试着每天至少给出一个答案!)

请注意,此代码表示需要根据您的场景进行调整的抽象模式

public class Base
{
  public string BaseString { get; set; }
}

public class Derived : Base
{
  public string DerivedString { get; set; }
}

public static class SO4870831Extensions
{
  private static Dictionary<Type, Action<Base>> _helpers = 
    new Dictionary<Type,Action<Base>>();

  public static void Extension<TBase>(this TBase instance) 
    where TBase :Base
  {
    //see if we have a helper for the absolute type of the instance
    var derivedhelper = ResolveHelper<TBase>(instance);

    if (derivedhelper != null)
      derivedhelper(instance);
    else
      ExtensionHelper(instance);
  }

  public static void ExtensionHelper(this Base instance)
  {
    Console.WriteLine("Base string: {0}", 
      instance.BaseString ?? "[null]");
  }

  /// <summary>
  /// By Default this method is resolved dynamically, but is also 
  /// available explicitly.
  /// </summary>
  /// <param name="instance"></param>
  public static void ExtensionHelper(this Derived instance)
  {
    Console.WriteLine("Derived string: {0}", 
      instance.DerivedString ?? "[null]");
    //call the 'base' version - need the cast to avoid Stack Overflow(!)
    ((Base)instance).ExtensionHelper();
  }

  private static Action<Base> ResolveHelper<TBase>(TBase instance) 
    where TBase : Base
  {
    Action<Base> toReturn = null;
    Type instanceType = instance.GetType();
    if (_helpers.TryGetValue(instance.GetType(), out toReturn))
      return toReturn;  //could be null - that's fine

    //see if we can find a method in this class for that type
    //this could become more complicated, for example, reflecting
    //the type itself, or using attributes for richer metadata
    MethodInfo helperInfo = typeof(SO4870831Extensions).GetMethod(
      "BaseExtensionHelper", 
      BindingFlags.Public | BindingFlags.Static, 
      null, 
      new Type[] { instanceType }, 
      null);

    if (helperInfo != null)
    {
      ParameterExpression p1 = Expression.Parameter(typeof(Base), "p1");
      toReturn =
        Expression.Lambda<Action<Base>>(
        /* body */
          Expression.Call(
            helperInfo,
            Expression.Convert(p1, instanceType)),
        /* param */
          p1).Compile();
      _helpers.Add(instanceType, toReturn);
    }
    else
      //cache the null lookup so we don't expend energy doing it again
      _helpers.Add(instanceType, null);
    return toReturn;
  }
}
/// <summary>
/// Summary description for UnitTest1
/// </summary>
[TestClass]
public class UnitTest1
{
  [TestMethod]
  public void TestMethod1()
  {
    var a = new Base() { BaseString = "Base Only" };
    var b = new Derived() { DerivedString = "Derived", BaseString = "Base" };

    a.Extension();
    //Console output reads:
    //"Base String: Base Only"
    b.Extension();
    //Console output reads:
    //"Derived String: Derived"
    //"Base String: Base"
  }
公共类基
{
公共字符串基串{get;set;}
}
派生的公共类:基
{
公共字符串DerivedString{get;set;}
}
公共静态类SO4870831扩展
{
专用静态词典_helpers=
新字典();
公共静态无效扩展(此TBase实例)
其中TBase:Base
{
//查看是否有实例绝对类型的帮助器
var derivedheloper=resolveheloper(实例);
如果(derivedhelper!=null)
derivedhelper(实例);
其他的
ExtensionHelper(实例);
}
公共静态void ExtensionHelper(此基本实例)
{
WriteLine(“基字符串:{0}”,
instance.BaseString??“[null]”;
}
/// 
///默认情况下,此方法是动态解析的,但也是
///明确提供。
/// 
/// 
公共静态void ExtensionHelper(此派生实例)
{
WriteLine(“派生字符串:{0}”,
instance.DerivedString??“[null]”;
//调用“基本”版本-需要强制转换以避免堆栈溢出(!)
((基本)实例);
}
私有静态操作ResolveHelper(TBase实例)
其中TBase:Base
{
返回操作=null;
类型instanceType=instance.GetType();
if(_helpers.TryGetValue(instance.GetType(),out-toReturn))
return toReturn;//可以为null-没关系
//看看我们是否能在此类中找到该类型的方法
//这可能会变得更复杂,例如,反射
//类型本身,或使用更丰富元数据的属性
MethodInfo helperInfo=typeof(SO4870831Extensions).GetMethod(
“BaseExtensionHelper”,
BindingFlags.Public | BindingFlags.Static,
无效的
新类型[]{instanceType},
无效);
if(helperInfo!=null)
{
ParameterExpression p1=表达式参数(typeof(Base),“p1”);
返回=
Lambda(
/*身体*/
表情,打电话(
助手信息,
Expression.Convert(p1,instanceType)),
/*param*/
p1).编译();
_添加(instanceType,toReturn);
}
其他的
//缓存空查找,这样我们就不会再次花费精力了
_助手
class Repository<T> : IRepository<T>
{

   public virtual T FindById(int id)
   { ... }
}

class FruitRepository<T> : Repository<T> where T : Fruit
{
   public override T FindById(int id)
   {  ... }
}

class AppleRepository : FruitRepository<Apple>
{
   public override T FindById(int id)
   {  ... }
}
// ideally you would resolve this via constructor injection, but whatever.
var repository = container.Resolve<IRepository<Apple>>(); 
var apple = repository.FindByID(1);
public class Base
{
  public string BaseString { get; set; }
}

public class Derived : Base
{
  public string DerivedString { get; set; }
}

public static class SO4870831Extensions
{
  private static Dictionary<Type, Action<Base>> _helpers = 
    new Dictionary<Type,Action<Base>>();

  public static void Extension<TBase>(this TBase instance) 
    where TBase :Base
  {
    //see if we have a helper for the absolute type of the instance
    var derivedhelper = ResolveHelper<TBase>(instance);

    if (derivedhelper != null)
      derivedhelper(instance);
    else
      ExtensionHelper(instance);
  }

  public static void ExtensionHelper(this Base instance)
  {
    Console.WriteLine("Base string: {0}", 
      instance.BaseString ?? "[null]");
  }

  /// <summary>
  /// By Default this method is resolved dynamically, but is also 
  /// available explicitly.
  /// </summary>
  /// <param name="instance"></param>
  public static void ExtensionHelper(this Derived instance)
  {
    Console.WriteLine("Derived string: {0}", 
      instance.DerivedString ?? "[null]");
    //call the 'base' version - need the cast to avoid Stack Overflow(!)
    ((Base)instance).ExtensionHelper();
  }

  private static Action<Base> ResolveHelper<TBase>(TBase instance) 
    where TBase : Base
  {
    Action<Base> toReturn = null;
    Type instanceType = instance.GetType();
    if (_helpers.TryGetValue(instance.GetType(), out toReturn))
      return toReturn;  //could be null - that's fine

    //see if we can find a method in this class for that type
    //this could become more complicated, for example, reflecting
    //the type itself, or using attributes for richer metadata
    MethodInfo helperInfo = typeof(SO4870831Extensions).GetMethod(
      "BaseExtensionHelper", 
      BindingFlags.Public | BindingFlags.Static, 
      null, 
      new Type[] { instanceType }, 
      null);

    if (helperInfo != null)
    {
      ParameterExpression p1 = Expression.Parameter(typeof(Base), "p1");
      toReturn =
        Expression.Lambda<Action<Base>>(
        /* body */
          Expression.Call(
            helperInfo,
            Expression.Convert(p1, instanceType)),
        /* param */
          p1).Compile();
      _helpers.Add(instanceType, toReturn);
    }
    else
      //cache the null lookup so we don't expend energy doing it again
      _helpers.Add(instanceType, null);
    return toReturn;
  }
}
/// <summary>
/// Summary description for UnitTest1
/// </summary>
[TestClass]
public class UnitTest1
{
  [TestMethod]
  public void TestMethod1()
  {
    var a = new Base() { BaseString = "Base Only" };
    var b = new Derived() { DerivedString = "Derived", BaseString = "Base" };

    a.Extension();
    //Console output reads:
    //"Base String: Base Only"
    b.Extension();
    //Console output reads:
    //"Derived String: Derived"
    //"Base String: Base"
  }