我正在尝试在泛型类上定义一个仅限于特定类型的方法。我想出了这个:
interface IHasId
{
int Id { get; }
}
public class Foo<T>
{
private List<T> children;
public IHasId GetById(int id)
{
foreach (var child in children.Cast<IHasId>())
{
if (child.Id == id)
{
return child;
}
}
return null;
}
}
它会工作,但它看起来像代码味道......似乎应该有一种方法让编译器强制执行此操作。类似的东西:
public class Foo<T>
{
public IHasId GetById<TWithId>(int id) where TWithId : IHasId {}
}
或者,甚至更好:
public class Foo<T>
{
public IHasId GetById(int id) where T : IHasId {}
}
我看到了一些与Java相关的帖子,其中一篇专门讨论将T限制为枚举,但没有直接关注点。
答案 0 :(得分:3)
您不能拥有基于单一类型的可选方法。但是,您可以使用继承来使其工作。
以下是:
public interface IHasId
{
int Id { get; }
}
public class Foo<T>
{
protected List<T> children;
}
public class FooHasId<T> : Foo<T> where T : IHasId
{
public IHasId GetById(int id)
{
foreach (var child in children)
{
if (child.Id == id)
{
return child;
}
}
return null;
}
}
使用C#6 FooHasId
可以缩短为:
public class FooHasId<T> : Foo<T> where T : IHasId
{
public IHasId GetById(int id) => this.children.FirstOrDefault(x => x.Id == id);
}
答案 1 :(得分:1)
您可以向泛型添加多个约束,例如,此处GetById方法中的泛型必须为T和IHasId:
using System.Collections.Generic;
using System.Linq;
public interface IHasId
{
public int Id { get; }
}
public class Foo<T>
{
private List<T> children;
public TItem GetById<TItem>(int id)
where TItem : T, IHasId
{
return children.Where(t => t is TItem).Cast<TItem>().FirstOrDefault(t => t.Id == id);
}
}
答案 2 :(得分:0)
您可以使用方法扩展来执行此操作,但是,您无法混合列表中的类型,因为方法扩展必须是显式的。
如果您使用类型作为通用类型,则扩展方法无法识别接口,因此您需要非常具体。
namespace Examples
{
class Program
{
static void Main(string[] args)
{
Foo<IHasName> nameList = new Foo<IHasName>(new List<IHasName>()
{
new ObjectWithName("banana"),
new ObjectWithName("apple")
});
Foo<IHasId> idList = new Foo<IHasId>(new List<IHasId>()
{
new ObjectWithId(1),
new ObjectWithId(2),
new ObjectWithId(3)
});
var obj1 = nameList.GetByName("banana");
var obj2 = idList.GetById(2);
}
}
public class ObjectWithName : IHasName
{
public string Name { get; }
public ObjectWithName(string name)
{
Name = name;
}
}
public class ObjectWithId : IHasId
{
public int Id { get; }
public ObjectWithId(int id)
{
Id = id;
}
}
public interface IHasName
{
string Name { get; }
}
public interface IHasId
{
int Id { get; }
}
public class Foo<T> : IEnumerable<T>
{
private IList<T> children;
public Foo(IList<T> collection)
{
children = collection;
}
public IEnumerator<T> GetEnumerator()
{
return children.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return children.GetEnumerator();
}
}
public static class FooExtensions
{
public static IHasId GetById(this Foo<IHasId> foo, int id)
{
return foo.FirstOrDefault(c => c.Id == id);
}
public static IHasName GetByName(this Foo<IHasName> foo, string name)
{
return foo.FirstOrDefault(c => c.Name == name);
}
}
}
答案 3 :(得分:-1)
也许你可以使用2泛型,那么你可以在不同的级别应用约束?如果您尝试在违反约束的类型上调用GetById,我不确定它在编译或运行时是如何工作的。
public interface IHasId
{
int Id { get; }
}
public class Foo<T>
{
private List<T> children;
public IHasId GetById<SubT>(int id) where SubT : IHasId
{
foreach (var child in children.Cast<IHasId>())
{
if (child.Id == id)
{
return child;
}
}
return null;
}
}
答案 4 :(得分:-2)
您可以应用约束并像这样执行(使用一点linq,您的方法将如下所示):
public interface IHasId
{
int Id { get; }
}
public class Foo<T> where T : IHasId
{
private List<T> children;
public IHasId GetById(int id)
{
return this.children.FirstOrDefault(x => x.Id == id);
}
}
<强>更新强>
我将上面的答案保留为原因,因为这清楚地回答了OP的问题,而且任何低调的人都不理解泛型。
@Enigmativity在回答我的一条评论时发表了评论:不,你没有回答这个问题。 OP希望方法受约束 - 而不是整个类。他希望T成为任何类型。
如果OP希望方法仅受约束而不是整个类,则答案如下:
仅限制方法
您不能仅对方法应用约束,因为在构造类时,必须关闭泛型。换句话说,您要在T
字段中存储children
(s)类型,类型T
必须在类级别关闭。因此,类型T
必须在类级别进行约束。
如果您希望该方法只是通用的,那么您不能在类级别使用类型T
。您的类不必具有通用方法的通用性。您仍然可以在课程中使用通用方法,如下所示:
public class Foo
{
// The children list is not of type T, but it has been closed with
// type SomeClass
private List<SomeClass> children;
// Here we are saying this method will work so long as it is called with
// any T that implements IHasId
// The class is not generic, but the method is
public bool IsIdOver100<T>(T hasId) where T : IHasId
{
return hasId.Id > 100;
}
}
public class SomeClass : IHasId
{
public int Id { get { /*...code*/ } }
}
仅供参考,@ engimativity的答案也是在类级别应用约束,但指示使用继承。那仍将在类级别应用约束,继承没有区别。
@kblok在对这个答案的评论中也发表了类似的评论:
他询问如何在方法中应用约束,你没有回答这个问题
这就是解决方案,kblok建议:
public TItem GetById<TItem>(int id)
where TItem : T, IHasId
{
return children.Select(t => t is TItem).Cast<TItem>().FirstOrDefault(t => t.Id == id);
}
上面有两个问题:1。方法是强制转换,因此它不是通用的。 2.约束是TItem : T
这是没有意义的,因为T
不是封闭类型,并且T
可以没有约束。我甚至不确定为什么编译器甚至会允许它,但可能有充分的理由。如果T
在类级别(例如where T : IAnotherInterface
)有约束,那么在方法级别放置一个约束是有意义的,该约束表示:where TItem : T, IHasId
但仍然使方法保持非如果正在进行施法,那就是通用的。
OP的更多信息
如果你想继承Foo
类,你可以在关闭或不关闭的情况下继续这样做。以下是关闭它的示例:
public class FooOfSomeClass : Foo<SomeClass>
{
// you can have addition methods, properties stuff here like any other inheriting class
}