我如何使用泛型将这个非常简单的 .NET 代码修复为 return 强类型结果?
How can I fix this really simple .NET code using Generics to return the strongly typed result?
注意:下面的所有代码 exists in this .NET Fiddle。
我(再次)在尝试理解 Generics/covariance/contravariance 时遇到了问题。 :(
这太让我难受了-所以请善待我。
所以,我有这家虚构的宠物店,我正在尝试轻松 在我们的宠物库存中搜索宠物。
主要搜索 class SearchMagic
是我所有的 greif 所在。我想说的是:
var pet = searchMagic.SearchForAPet("Fido");
它会..在这种情况下 return 一个 Dog
这是 Fido.
现在最重要的是我如何实现 class SearchMagic
。这是它首先搜索 Cat
,然后是 Dog
的聪明之处。问题是,如果找到的话,我似乎无法找到 return 猫或狗实例的方法。它一直抱怨Cannot implicitly convert...
我一直试图对代码说:至少,我需要一个 SearchResult<IAnimal>
returned。因此,如果那是具体的 Cat
或 Dog
,那就太棒了。否则为 null。然后对于 调用 该方法并获得 returned 结果的代码,我唯一可以 做 的结果,是 SearchResult<IAnimal>
中定义的任何内容。因此,如果我的 Cat
实例有一些 cat 特定的属性或方法,那么运气不好,我不能在 result
上调用它们,因为结果是 SearchResult<IAnimal>
而只有 IAnimal
东西暴露了。
这是代码,内联一些注释以显示错误发生的位置...
请告诉我 ze 代码。
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var searchMagic = new SearchMagic();
var animal = searchMagic.SearchForAPet("Fido");
}
// ***********************************
// ** Interfaces / contracts **
// ***********************************
public interface IAnimal
{
string Name { get; set; }
}
public interface ISearchService<TAnimal> where TAnimal : IAnimal
{
SearchResult<TAnimal> SearchInventory(string query);
}
public class SearchResult<TAnimal> where TAnimal : IAnimal
{
TAnimal Pet { get; set; }
decimal Price { get; set; }
}
// ***********************************
// ** Concrete implementations **
// ***********************************
public class Cat : IAnimal
{
public string Name { get; set; }
}
public class Dog : IAnimal
{
public string Name { get; set; }
}
public class CatSearchService : ISearchService<Cat>
{
public SearchResult<Cat> SearchInventory(string query)
{
return new SearchResult<Cat>();
}
}
public class DogSearchService : ISearchService<Dog>
{
public SearchResult<Dog> SearchInventory(string query)
{
return new SearchResult<Dog>();
}
}
public class SearchMagic
{
private CatSearchService _catSearchService = new CatSearchService();
private DogSearchService _dogSearchService = new DogSearchService();
public SearchResult<IAnimal> SearchForAPet(string query)
{
var cat = _catSearchService.SearchInventory(query);
if (cat != null)
{
// ** ERROR: Cannot implicitly convert type 'Program.SearchResult<Program.Cat>' to 'Program.SearchResult<Program.IAnimal>'
return cat;
}
var dog = _dogSearchService.SearchInventory(query);
if (dog != null)
{
// ** ERROR: Cannot implicitly convert type 'Program.SearchResult<Program.Dog>' to 'Program.SearchResult<Program.IAnimal>'
return dog;
}
return null;
}
}
}
如果您希望 SearchForAPet
方法能够 return 可以是 "dog search result" 或 "cat search result" 的 "search result",它需要return 协变接口,而不是具体类型:
public interface ISearchResult<out TAnimal> where TAnimal: IAnimal
{
TAnimal Pet { get; }
decimal Price { get; }
}
public class SearchResult<TAnimal> : ISearchResult<TAnimal> where TAnimal : IAnimal
{
public TAnimal Pet { get; set; }
public decimal Price { get; set; }
}
...
public ISearchResult<IAnimal> SearchForAPet(string query)
{
...
}
也没有找到任何其他方法来保持结构完整。您需要类似以下内容才能从中获取搜索结果:
public interface ISearchResult<out TAnimal> where TAnimal : IAnimal
{
TAnimal Pet { get; }
decimal Price { get; }
}
在任何情况下,如果您想选择退出 out
,您需要在执行 return 时相应地进行转换。
原因是:
这里看起来像是另一个经典的协变、逆变和不变问题。根据 doc here,您可以使用协变,这将使您能够使用比最初指定的派生类型更多的类型,反之亦然,但您不可能期望这样做:
public class Program
{
public static void Main()
{
List<Derived> dlist = new List<Derived>();
List<Base> bIEnum = dlist;
}
}
但是你可以:
public class Program
{
public static void Main()
{
List<Derived> dlist = new List<Derived>();
IEnumerable<Base> bIEnum = dlist;
}
}
因此,拥有 ISearchResult 或 ISearchResult 是您最好的选择
注意:下面的所有代码 exists in this .NET Fiddle。
我(再次)在尝试理解 Generics/covariance/contravariance 时遇到了问题。 :(
这太让我难受了-所以请善待我。
所以,我有这家虚构的宠物店,我正在尝试轻松 在我们的宠物库存中搜索宠物。
主要搜索 class SearchMagic
是我所有的 greif 所在。我想说的是:
var pet = searchMagic.SearchForAPet("Fido");
它会..在这种情况下 return 一个 Dog
这是 Fido.
现在最重要的是我如何实现 class SearchMagic
。这是它首先搜索 Cat
,然后是 Dog
的聪明之处。问题是,如果找到的话,我似乎无法找到 return 猫或狗实例的方法。它一直抱怨Cannot implicitly convert...
我一直试图对代码说:至少,我需要一个 SearchResult<IAnimal>
returned。因此,如果那是具体的 Cat
或 Dog
,那就太棒了。否则为 null。然后对于 调用 该方法并获得 returned 结果的代码,我唯一可以 做 的结果,是 SearchResult<IAnimal>
中定义的任何内容。因此,如果我的 Cat
实例有一些 cat 特定的属性或方法,那么运气不好,我不能在 result
上调用它们,因为结果是 SearchResult<IAnimal>
而只有 IAnimal
东西暴露了。
这是代码,内联一些注释以显示错误发生的位置...
请告诉我 ze 代码。
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var searchMagic = new SearchMagic();
var animal = searchMagic.SearchForAPet("Fido");
}
// ***********************************
// ** Interfaces / contracts **
// ***********************************
public interface IAnimal
{
string Name { get; set; }
}
public interface ISearchService<TAnimal> where TAnimal : IAnimal
{
SearchResult<TAnimal> SearchInventory(string query);
}
public class SearchResult<TAnimal> where TAnimal : IAnimal
{
TAnimal Pet { get; set; }
decimal Price { get; set; }
}
// ***********************************
// ** Concrete implementations **
// ***********************************
public class Cat : IAnimal
{
public string Name { get; set; }
}
public class Dog : IAnimal
{
public string Name { get; set; }
}
public class CatSearchService : ISearchService<Cat>
{
public SearchResult<Cat> SearchInventory(string query)
{
return new SearchResult<Cat>();
}
}
public class DogSearchService : ISearchService<Dog>
{
public SearchResult<Dog> SearchInventory(string query)
{
return new SearchResult<Dog>();
}
}
public class SearchMagic
{
private CatSearchService _catSearchService = new CatSearchService();
private DogSearchService _dogSearchService = new DogSearchService();
public SearchResult<IAnimal> SearchForAPet(string query)
{
var cat = _catSearchService.SearchInventory(query);
if (cat != null)
{
// ** ERROR: Cannot implicitly convert type 'Program.SearchResult<Program.Cat>' to 'Program.SearchResult<Program.IAnimal>'
return cat;
}
var dog = _dogSearchService.SearchInventory(query);
if (dog != null)
{
// ** ERROR: Cannot implicitly convert type 'Program.SearchResult<Program.Dog>' to 'Program.SearchResult<Program.IAnimal>'
return dog;
}
return null;
}
}
}
如果您希望 SearchForAPet
方法能够 return 可以是 "dog search result" 或 "cat search result" 的 "search result",它需要return 协变接口,而不是具体类型:
public interface ISearchResult<out TAnimal> where TAnimal: IAnimal
{
TAnimal Pet { get; }
decimal Price { get; }
}
public class SearchResult<TAnimal> : ISearchResult<TAnimal> where TAnimal : IAnimal
{
public TAnimal Pet { get; set; }
public decimal Price { get; set; }
}
...
public ISearchResult<IAnimal> SearchForAPet(string query)
{
...
}
也没有找到任何其他方法来保持结构完整。您需要类似以下内容才能从中获取搜索结果:
public interface ISearchResult<out TAnimal> where TAnimal : IAnimal
{
TAnimal Pet { get; }
decimal Price { get; }
}
在任何情况下,如果您想选择退出 out
,您需要在执行 return 时相应地进行转换。
原因是:
这里看起来像是另一个经典的协变、逆变和不变问题。根据 doc here,您可以使用协变,这将使您能够使用比最初指定的派生类型更多的类型,反之亦然,但您不可能期望这样做:
public class Program
{
public static void Main()
{
List<Derived> dlist = new List<Derived>();
List<Base> bIEnum = dlist;
}
}
但是你可以:
public class Program
{
public static void Main()
{
List<Derived> dlist = new List<Derived>();
IEnumerable<Base> bIEnum = dlist;
}
}
因此,拥有 ISearchResult 或 ISearchResult 是您最好的选择