C# 泛型与接口的实际优势
在这种情况下,使用泛型与接口的实际优势是什么:C# 泛型与接口的实际优势,c#,generics,polymorphism,C#,Generics,Polymorphism,在这种情况下,使用泛型与接口的实际优势是什么: void MyMethod(IFoo f) { } void MyMethod<T>(T f) : where T : IFoo { } voidmymethod(ifoof) { } void MyMethod(tf):其中T:IFoo { } 也就是说,在MyMethod中,您可以做哪些在非通用版本中无法做到的事情?我在寻找一个实际的例子,我知道理论上的差异是什么 我知道在MyMethod中,T将是具体的类型,但尽管如此,我
void MyMethod(IFoo f)
{
}
void MyMethod<T>(T f) : where T : IFoo
{
}
voidmymethod(ifoof)
{
}
void MyMethod(tf):其中T:IFoo
{
}
也就是说,在MyMethod
中,您可以做哪些在非通用版本中无法做到的事情?我在寻找一个实际的例子,我知道理论上的差异是什么
我知道在
MyMethod
中,T将是具体的类型,但尽管如此,我只能在方法体中将其用作IFoo。那么什么才是真正的优势呢?泛型版本允许您使用任何类型作为T-出于某种原因,您可以通过使用where子句限制它,而非泛型版本只支持实现IFoo的东西
另一个(可能更好)的问题是-这两个选项等效吗?接口方法将为您提供类型为
IFoo
的f
,而通用版本将为您提供类型T
,并带有T
必须实现IFoo
的约束
第二种方法允许您根据T
进行某种类型的查找,因为您可以使用具体的类型。在这种特殊情况下,没有任何好处。通常,您不会在方法级别指定它,而是在类级别指定它。例如:
public interface IFoo {
void DoSomethingImportant();
}
public class MyContainer<T> where T : IFoo {
public void Add(T something){
something.DoSomethingImportant();
AddThisThing(something);
}
public T Get() {
T theThing = GetSomeKindOfThing();
return theThing;
}
}
- 通过接口调用方法要比直接在具体类型上调用慢
- 如果实现
的类型是值类型,则非泛型版本将装箱参数的值,装箱可能会对性能产生负面影响(特别是如果您经常调用此方法)IFoo
- 如果您的方法返回值,则泛型版本可以返回
而不是T
,如果您需要在结果上调用T的方法,这非常方便IFoo
- 做这样的事情更容易:
void MyMethod<T>(T f) where T : IFoo, new() {
var t1 = new T();
var t2 = default(T);
// Etc...
}
正如其他地方提到的,一个优点是,如果您返回一个值,就能够返回特定类型的IFoo类型。但是由于您的问题是关于
voidmymethod(ifoof)
,我想给出一个实际的例子,说明至少一种情况,在这种情况下,使用泛型方法比使用接口更有意义(对我来说)。(是的,我在这方面花了一些时间,但我想尝试一些不同的想法。:D)
有两块代码,第一块只是泛型方法本身和一些上下文,第二块是示例的完整代码,包括大量注释,其中包括关于此实现与等效非泛型实现之间可能存在差异的注释,以及我在实现时尝试的各种不起作用的东西,以及我所作的各种选择的说明等;博士和所有这些
概念
公共类FooChains:Dictionary{}
//管理我们的食品及其连锁店。非常重要的食物链。
公共类FoodManager
{
私有FooChains myChainList=新FooChains();
//void MyMethod(tf),其中T:IFoo
void CopyAndChainFoo(TFoo fromFoo),其中TFoo:IFoo
{
TFoo-toFoo;
试一试{
//从相同类型的foo创建foo
toFoo=(TFoo)fromFoo.MakeTyped(efoopts.ForChain);
}
捕获(例外情况除外){
//嘿!那可不是同一种食物!
抛出新的FooChainTypeMismatch(typeof(TFoo),fromFoo,Ex);
}
//链接到fromFoo的特定类型的Foo的列表
列出类型食物;
如果(!myChainList.Keys.Contains(fromFoo))
{
//那里没有foo!列出一个列表并将它们连接到fromFoo
typedChain=新列表();
添加(fromFoo,(IEnumerable)typedChain);
}
其他的
//哦,太好了,链条是存在的,呸!
typedChain=(List)myChainList[fromFoo];
//将新的foo添加到连接的foo链中
类型链添加(toFoo);
//我们完了!
}
}
血淋淋的细节
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
命名空间IFooedYouOnce
{
//伊福
//
//它的个性是如此具有磁性,它的硬盘被擦除。
//它可以调试其他代码…通过实际调试其他代码。
//它可以用C语言说哈斯克尔语。
//
//它是世界上最有趣的界面。
公共接口IFoo
{
//虽然最终没有使用这个,但它仍然存在,因为有些
//没有它,支持派生类的方法看起来很愚蠢。
布尔坎查因{get;}
字符串标识符{get;}
//希望在派生方法中对此设置约束
//以确保类型安全,但必须使用异常。
//利斯科夫·亚达·亚达·亚达。。。
IFoo-MakeTyped(efoopts-fooOpts);
}
//在这里使用IEnumerable来利用协方差;
//我们可以有派生的foo列表,只需回溯和
//forth用于添加或如果需要使用派生接口。
//因为可能会有
//您可以在链集合上作为
//这样就有了一个位置,而不是说,
//在FooManager中实现这一切
公共类FooChains:字典{}
//管理食物。非常重要的食物。
公共类FoodManager
{
私有FooChains myChainList=新FooChains();
//可能会在此处添加一个新的()约束,以使
//创建更容易一点;可以删除整个MakeTypted
//方法,但试图从
//这个问题。
void CopyAndChainFoo(TFoo fromFoo),其中TFoo:IFoo
//void MyMethod(tf),其中T:IFoo
{
TFoo-toFoo;
Bar b = (Bar) m.Get();
void MyMethod<T>(T f) where T : IFoo, new() {
var t1 = new T();
var t2 = default(T);
// Etc...
}
interface IFoo {
}
interface IBar {
}
class FooBar : IFoo, IBar {
}
void MyMethod<T>(T f) where T : IFoo, IBar {
}
void Test() {
FooBar fb = new FooBar();
MyMethod(fb);
}
interface IFoo {
}
interface IBar {
}
interface IFooBar : IFoo, IBar {
}
class FooBar : IFooBar {
}
void MyMethod(IFooBar f) {
}
void Test() {
FooBar fb = new FooBar();
MyMethod(fb);
}
public class FooChains : Dictionary<IFoo, IEnumerable<IFoo>> { }
// to manage our foos and their chains. very important foo chains.
public class FooManager
{
private FooChains myChainList = new FooChains();
// void MyMethod<T>(T f) where T : IFoo
void CopyAndChainFoo<TFoo>(TFoo fromFoo) where TFoo : IFoo
{
TFoo toFoo;
try {
// create a foo from the same type of foo
toFoo = (TFoo)fromFoo.MakeTyped<TFoo>(EFooOpts.ForChain);
}
catch (Exception Ex) {
// hey! that wasn't the same type of foo!
throw new FooChainTypeMismatch(typeof(TFoo), fromFoo, Ex);
}
// a list of a specific type of foos chained to fromFoo
List<TFoo> typedFoos;
if (!myChainList.Keys.Contains(fromFoo))
{
// no foos there! make a list and connect them to fromFoo
typedChain = new List<TFoo>();
myChainList.Add(fromFoo, (IEnumerable<IFoo>)typedChain);
}
else
// oh good, the chain exists, phew!
typedChain = (List<TFoo>)myChainList[fromFoo];
// add the new foo to the connected chain of foos
typedChain.Add(toFoo);
// and we're done!
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace IFooedYouOnce
{
// IFoo
//
// It's personality is so magnetic, it's erased hard drives.
// It can debug other code... by actually debugging other code.
// It can speak Haskell... in C.
//
// It *is* the most interesting interface in the world.
public interface IFoo
{
// didn't end up using this but it's still there because some
// of the supporting derived classes look silly without it.
bool CanChain { get; }
string FooIdentifier { get; }
// would like to place constraints on this in derived methods
// to ensure type safety, but had to use exceptions instead.
// Liskov yada yada yada...
IFoo MakeTyped<TFoo>(EFooOpts fooOpts);
}
// using IEnumerable<IFoo> here to take advantage of covariance;
// we can have lists of derived foos and just cast back and
// forth for adding or if we need to use the derived interfaces.
// made it into a separate class because probably there will be
// specific operations you can do on the chain collection as a
// whole so this way there's a spot for it instead of, say,
// implementing it all in the FooManager
public class FooChains : Dictionary<IFoo, IEnumerable<IFoo>> { }
// manages the foos. very highly important foos.
public class FooManager
{
private FooChains myChainList = new FooChains();
// would perhaps add a new() constraint here to make the
// creation a little easier; could drop the whole MakeTyped
// method. but was trying to stick with the interface from
// the question.
void CopyAndChainFoo<TFoo>(TFoo fromFoo) where TFoo : IFoo
// void MyMethod<T>(T f) where T : IFoo
{
TFoo toFoo;
// without generics, I would probably create a factory
// method on one of the base classes that could return
// any type, and pass in a type. other ways are possible,
// for instance, having a method which took two IFoos,
// fromFoo and toFoo, and handling the Copy elsewhere.
// could have bypassed this try/catch altogether because
// MakeTyped functions throw if the types are not equal,
// but wanted to make it explicit here. also, this gives
// a more descriptive error which, in general, I prefer
try
{
// MakeTyped<TFoo> was a solution to allowing each TFoo
// to be in charge of creating its own objects
toFoo =
(TFoo)fromFoo.MakeTyped<TFoo>(EFooOpts.ForChain);
}
catch (Exception Ex) {
// tried to eliminate the need for this try/catch, but
// didn't manage. can't constrain the derived classes'
// MakeTyped functions on their own types, and didn't
// want to change the constraints to new() as mentioned
throw
new FooChainTypeMismatch(typeof(TFoo), fromFoo, Ex);
}
// a list of specific type foos to hold the chain
List<TFoo> typedFoos;
if (!myChainList.Keys.Contains(fromFoo))
{
// we just create a new one and link it to the fromFoo
// if none already exists
typedFoos = new List<TFoo>();
myChainList.Add(fromFoo, (IEnumerable<IFoo>)typedFoos);
}
else
// otherwise get the existing one; we are using the
// IEnumerable to hold actual List<TFoos> so we can just
// cast here.
typedFoos = (List<TFoo>)myChainList[fromFoo];
// add it in!
typedFoos.Add(toFoo);
}
}
[Flags]
public enum EFooOpts
{
ForChain = 0x01,
FullDup = 0x02,
RawCopy = 0x04,
Specialize = 0x08
}
// base class, originally so we could have the chainable/
// non chainable distinction but that turned out to be
// fairly pointless since I didn't use it. so, just left
// it like it was anyway so I didn't have to rework all
// the classes again.
public abstract class FooBase : IFoo
{
public string FooIdentifier { get; protected set; }
public abstract bool CanChain { get; }
public abstract IFoo MakeTyped<TFoo>(EFooOpts parOpts);
}
public abstract class NonChainableFoo : FooBase
{
public override bool CanChain { get { return false; } }
}
public abstract class ChainableFoo : FooBase
{
public override bool CanChain { get { return true; } }
}
// not much more interesting to see here; the MakeTyped would
// have been nicer not to exist, but that would have required
// a new() constraint on the chains function.
//
// or would have added "where TFoo : MarkIFoo" type constraint
// on the derived classes' implementation of it, but that's not
// allowed due to the fact that the constraints have to derive
// from the base method, which had to exist on the abstract
// classes to implement IFoo.
public class MarkIFoo : NonChainableFoo
{
public MarkIFoo()
{ FooIdentifier = "MI_-" + Guid.NewGuid().ToString(); }
public override IFoo MakeTyped<TFoo>(EFooOpts fooOpts)
{
if (typeof(TFoo) != typeof(MarkIFoo))
throw new FooCopyTypeMismatch(typeof(TFoo), this, null);
return new MarkIFoo(this, fooOpts);
}
private MarkIFoo(MarkIFoo fromFoo, EFooOpts parOpts) :
this() { /* copy MarkOne foo here */ }
}
public class MarkIIFoo : ChainableFoo
{
public MarkIIFoo()
{ FooIdentifier = "MII-" + Guid.NewGuid().ToString(); }
public override IFoo MakeTyped<TFoo>(EFooOpts fooOpts)
{
if (typeof(TFoo) != typeof(MarkIIFoo))
throw new FooCopyTypeMismatch(typeof(TFoo), this, null);
return new MarkIIFoo(this, fooOpts);
}
private MarkIIFoo(MarkIIFoo fromFoo, EFooOpts parOpts) :
this() { /* copy MarkTwo foo here */ }
}
// yep, really, that's about all.
public class FooException : Exception
{
public Tuple<string, object>[] itemDetail { get; private set; }
public FooException(
string message, Exception inner,
params Tuple<string, object>[] parItemDetail
) : base(message, inner)
{
itemDetail = parItemDetail;
}
public FooException(
string msg, object srcItem, object destType, Exception inner
) : this(msg, inner,
Tuple.Create("src", srcItem), Tuple.Create("dtype", destType)
) { }
}
public class FooCopyTypeMismatch : FooException
{
public FooCopyTypeMismatch(
Type reqDestType, IFoo reqFromFoo, Exception inner
) : base("copy type mismatch", reqFromFoo, reqDestType, inner)
{ }
}
public class FooChainTypeMismatch : FooException
{
public FooChainTypeMismatch(
Type reqDestType, IFoo reqFromFoo, Exception inner
) : base("chain type mismatch", reqFromFoo, reqDestType, inner)
{ }
}
}
// I(Foo) shot J.R.!
class MyClass : IDisposable {
public void Dispose() {
if (m_field1 != null) {
m_field1.Dispose();
m_field1 = null;
}
if (m_field2 != null) {
m_field2.Dispose();
m_field2 = null;
}
// etc
}
}
class MyClass : IDisposable {
static void IfNotNullDispose(ref IDisposable disposable) {
if (disposable != null) {
disposable.Dispose();
disposable = null;
}
}
public void Dispose() {
IfNotNullDispose(ref m_field1);
IfNotNullDispose(ref m_field2);
// etc
}
}
static void IfNotNullDispose<T>(ref T disposable) where T: class, IDisposable {
if (disposable != null) {
disposable.Dispose();
disposable = null;
}
}
using System;
using System.Diagnostics;
namespace test
{
class MainApp
{
static void Main()
{
Foo f = new Foo();
IFoo f2 = f;
// JIT warm-up
f.Bar();
f2.Bar();
int N = 10000000;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < N; i++)
{
f2.Bar();
}
sw.Stop();
Console.WriteLine("Through interface: {0:F2}", sw.Elapsed.TotalMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < N; i++)
{
f.Bar();
}
sw.Stop();
Console.WriteLine("Direct call: {0:F2}", sw.Elapsed.TotalMilliseconds);
Console.Read();
}
interface IFoo
{
void Bar();
}
class Foo : IFoo
{
public virtual void Bar()
{
}
}
}
}