问题描述
我对泛型有一点疑问.
我想我已经知道答案了,如果是这样的话,我会很沮丧.但希望你们中的一些人能证明我是错误的:)
我有一些泛型接口和一些泛型类实现.
问题是我需要属性,而属性无法处理泛型!
因此我无法知道什么< T>用于我的接口和类.相反,我有我的Interface< T>从非通用接口派生而来...不幸的是,非通用性像非通用蛇一样在我的类结构中滑行!
我已经做出了使用我的通用接口的良好工作类,它们在没有属性的情况下也可以使用.因此,完全放弃泛型是没有选择的.
但是,是否可以将BaseInterface转换为DerivedInterface< T> ;?当您仅在运行时知道T时,这也是可能的吗?
类似于DerivedInterface< myVar.GetType()> ;?
我的问题在傍晚可能有点晦涩难懂,所以如果我不清楚的话,请不要犹豫:)
谢谢!
下一次编辑后进行
在对代码进行非常仔细的重新考虑之后,我得出的结论是我的设计已破产(是的,即使我犯了错误; p)!我不需要从IDerived<int>
转换为IDerived<Object>
.但是我仍然想知道是否有可能.
感谢SAKryukov提供了非常详细的答案!真的有帮助! :)
经过一夜安眠并阅读了SAKryukovs的答案之后,我现在可以用比昨天晚上更好的语言来表达我的问题了.所以又来了.
在下面的代码段(与我自己的场景非常匹配)中,我必须将Something<int>
转换为Something<Object>
.我的看法是int
是Object
,因此此转换应该没有问题.错了!
正如SAKryukov所说的那样(如果我错了,他应该纠正我)是因为Something<int>
是Something<int>
的类型(而不是Something
而不是int
或Generic Something
).即使Object Something<int>
中的int Inherits
并非Something<Object>
中的Inherit
.
I have a little problem with Generics.
I think I already know the answer and if that is the case I am bummed out. But hopefully some of you can prove me wrong :)
I have some Generic Interfaces and some Generic Class Implementations.
The problem is that I need Attributes, and Attributes cannot handle Generics!
So I have no way of knowing what <T> is for my Interface and Classes. Instead I have my Interface<T> derive from a non-Generic Interface... Unfortunately the non-Genericness is slithering through my Class structure like a non-Generic snake!
I have already made good working Classes that use my Generic Interfaces and they are usable also without the Attributes. So dropping Generics alltogether is no option.
Is there a way however to convert a BaseInterface to a DerivedInterface<T>? Is this also possible when you only know T at runtime?
Something like DerivedInterface<myVar.GetType()>?
My question may be a bit cryptic in the late evening, so if I am not clear enough do not hesitate to ask :)
Thanks!
Edit after next edit:
After really close re-re-re-re-consideration of my code I have concluded that my design is busted (yes, even I make mistakes ;p )! I do need not to cast from IDerived<int>
to IDerived<Object>
. However I would still like to know if it is possible.
Thanks to SAKryukov for a very detailed answer! It really helped! :)
After having a good night sleep and reading SAKryukovs answer I can now put my question into better words than last night though. So here it goes again.
In the following code snippet, which closely matches my own scenario, I must convert a Something<int>
to a Something<Object>
. The way I see it is that int
is an Object
so this conversion should be no problem. Wrong!
As SAKryukov put it (and he should correct me if I am wrong) it is because Something<int>
is the type of Something<int>
(and not Something
and then an int
or a Generic Something
). And even though int Inherits
from Object Something<int>
does not Inherit
from Something<Object>
.
class Program
{
static void Main(string[] args)
{
DerivedSomething thing = new DerivedSomething();
if (CheckMustStop<int>(thing))
{
Console.WriteLine("Stop!");
}
else { Console.WriteLine("Don't stop!"); }
Console.ReadKey();
}
static bool CheckMustStop<t>(Something<t> thing)
{
// This is where stuff gets nasty!
// An int is an Object, but casting will not work!
// Mind you that I know T is an int. In my own code I do not know this.
Something<Object> tempThing = (Something<Object>)thing;
return tempThing.MustStop;
}
}
public class DerivedSomething : Something<int>
{
public void DoSomething(int item)
{
// Do stuff here, <t> is important for item.
}
public bool MustStop
{
// <t> does not matter!
get { return true; }
}
}
public interface Something<t>
{
void DoSomething(T item);
bool MustStop { get; }
}
如您所见,示例中的T
类型并不重要.因为不管是哪种类型的T
,它总是会有一个MustStop Property
,它并不关心T
的类型.这使我相信Something<T>
实际上需要一个non-Generic base Interface
才能检查任何类型的Something<T>
是否必须停止.
如:
As you see it does not matter which type T
is in the example. Because no matter which type T
is it will always have a MustStop Property
which does not care about the type of T
. This leads me to believe that Something<T>
actually needs a non-Generic base Interface
to be able to check if Something<T>
of whatever type must stop.
Such as this:
public interface Something<t> : Something
{
void DoSomething(T item);
}
// I can always cast to Something without knowing the type of T!
public interface Something
{
void DoSomething(Object item);
bool MustStop { get; }
}
这会产生新的问题,例如Something
仅在具有non-Generic DoSomething Method
时才能单独使用,并且事实上我几乎所有的Generic Interfaces
都需要non-Generic base Interface
.确实会使代码混乱:)
我希望这可以进一步澄清我的问题.非常感谢您的帮助:)
This creates new problems, such as Something
only being usable on its own when it has a non-Generic DoSomething Method
and the fact that I need a non-Generic base Interface
for almost all my Generic Interfaces
. Which really tends to clutter up code :)
I hope this further clarifies my problem. Any help is greatly appreciated :)
推荐答案
class Base {/*...*/}
class Derived : Base {/*...*/}
Base b = new Derived(/*...*/);
Derived d = (Derived)b;
没关系,类还是接口.仅需要class
或struct
来拥有一个实例来说明这种情况. (重要!请注意,结构也可以实现接口-并不是非常为人所知的事实.)请注意,由于b
具有运行时类型实际上是Derived
,因此只能进行这种转换.您只能将某些编译时类型的变量强制转换为实际的运行时类型或其基数之一.
最后,意识到这一点.通过反射可以看出,即使泛型类型不仅在编译期间而且在运行时都存在,泛型类型在对象中不存在.从本质上讲,所有对象始终都是某种完全实例化的类型.您可以拥有System.Collections.Generic.List<string>
的对象,而不能具有通用的List
类型的对象.使问题变得非常简单:
It does not matter, classes or interfaces. A class
or struct
is only needed to have an instance to demonstrate the case. (Important! Note that structures can also implement interfaces — not very well known fact.) Note that this cast is only possible because b
has a run-time type which is actually Derived
. You can only cast a variable of some compile-time type to the actual run-time type or one of its bases.
Finally, realize that. Even though generic type do exist not only during compile time but also during run-time, which can be seen via Reflection, generic types do not exist in objects. By their nature, all objects are always of some fully-instantiated type. You can have an object of System.Collections.Generic.List<string>
, never of the generic List
type. In makes the problem pretty simple:
interface IBase {
void BaseOperation();
//...
} //interface IBase
interface IDerived<OPERAND> : IBase {
OPERAND Sum(params OPERAND[] members);
OPERAND Product(params OPERAND[] members);
//...
} //interface IDerived
abstract class Arithmetic<OPERAND> : IDerived<OPERAND> {
void IBase.BaseOperation() { }
OPERAND IDerived<OPERAND>.Sum(params OPERAND[] members) { return ImplementSum(members); }
OPERAND IDerived<OPERAND>.Product(params OPERAND[] members) { return ImplementProduct(members); }
protected internal abstract OPERAND ImplementSum(params OPERAND[] members);
protected internal abstract OPERAND ImplementProduct(params OPERAND[] members);
protected internal OPERAND SomeAuxMethod() { return default(OPERAND); }
//...
} //class Arithmetic
class IntegerArithmetic : Arithmetic<int>, IDerived<int> {
protected internal override int ImplementSum(params int[] members) {
int result = 0;
foreach (int member in members) result += member;
return result;
}
protected internal override int ImplementProduct(params int[] members) {
int result = 0;
foreach (int member in members) result *= member;
return result;
}
//...
} //class IntegerArithmetic
class Test {
void TestMethod() {
IBase @object = new IntegerArithmetic();
//...
IDerived<int> integerArithmeticObject =
(IDerived<int>)@object;
int sum = integerArithmeticObject.Sum(new int[] {1, 12, 113, });
//will work!
}
} //class Test
上面显示的抽象方法的主要好处是实现了某些算法,这些算法不依赖于接口IDerived
表示的抽象类Arithmetic
中OPERAND
的具体类型.这些方法未显示.它们可以包括使用OPERANDS
容器,进行搜索,排序(需要通用的约束以便进行比较运算符),以及更多其他工作.特定于数字的操作推迟到实例化泛型类型的类.对于数字类型,此问题尤其困难,当将通用参数约束为类或(甚至更简单)接口时,此问题就容易得多.
据我了解,您在设计代码时遇到了通用"问题.好吧,我知道,这是很自然的,即使我不太了解您的问题.理解可能需要一些示例代码.但是,我希望更好地了解泛型和OOP的组合可以帮助您重新审视您的问题并改进问题.我也希望我的简单示例代码可以给您一些想法.
祝你好运,
The main benefit of the method of abstraction shown above is having implementation of some algorithms not depending on concrete type of OPERAND
in abstract class Arithmetic
represented by the interface IDerived
. Those methods are not shown; they can include working with containers of OPERANDS
, search, sorting (will need generic constraint to allow comparison operators) and a lot more. Numeric-specific operations a postponed to the classes instantiating generic types. This problems is particularly difficult with numeric types and is much easier when generic parameters are constraint to be classes or (even easier) interfaces.
As I can understand, you have "generic" problems with designing your code. Well, I understand, this is natural, even though I don''t really understand your problem. The understanding probably requires some sample code. However, I hope that better understanding of combination of generics and OOP could help you to have a fresh look at your problem and improve things. I also hope my simple sample code can give you some ideas.
Good luck,
public class DerivedSomething : ISomething<int> {
public void DoSomething(int item) {
// Do stuff here, <T> is important for item.
} //DoSomething
public bool MustStop {
// <T> does not matter!
get { return true; }
} //MustStop
} //class DerivedSomething
public interface ISomething<T> {
void DoSomething(T item);
bool MustStop { get; }
} //ISomething<T>
class Test {
void TestMethod() {
DerivedSomething thing = new DerivedSomething();
if (thing.MustStop)
Console.WriteLine("Stop!");
else
Console.WriteLine("Don't stop!");
} //TestMethod
} //class Test
我还将Something
重命名为ISomething
,以满足(好的)Microsoft接口命名约定.由于您错过了一个重要步骤,而这一步骤部分地破坏了通用接口的目的,因此存在一定的困惑.为了使继承链真正可行,您需要像这样:generic interface =>通用(可能是抽象的)类实现方法,与通用参数无关一组完整的实现类-使用我之前的答案中显示的想法.术语"class
"可以替换为"struct
".
这真是太神奇了:我刚刚制作了一个真正的弗洛伊德遗书"( http://en.wikipedia.org/wiki/Freudian_slip [ ^ ])!
在上一段中,我想键入(好的)Microsoft命名约定",但键入"goof Microsoft命名约定"!
不,它们真的很好,不是愚蠢的.我正在使用它们.
[END EDIT#1]
经过一番思考,我意识到我可以在上面的代码中看到另外两个问题.我在上面解释过:1)缺少仅实现接口的非通用部分的中间实现类;这样,您就不会在每种特定于泛型的特定实现中重复执行那些非泛型方法/属性.剩下的两个问题是:2)设计了在运行时使用的接口;从代码中删除所有接口-它将以完全相同的方式工作,为什么还要编写它们?您应该真正使用通过接口进行分类; 3)使用隐式接口实现(通过"public
");但是显式的接口实现要清晰得多.
我对问题(2)和(3)的解决方案需要更改用法,但是以一种实际上有益的方式证明使用接口是合理的,并且如果修复了问题,则缺少代码重用(所需的模式仅在需要时才有用有多个像DerivedSomething
这样的类,但是如果您尝试编写更多的类,您会发现MustStop
实现没有被重用);因此,将所有代码固定在一起的代码如下所示:
I also renamed Something
to ISomething
to meet (good) Microsoft naming conventions for interfaces. There is certain confusion due to the fact you miss one important step which partially defeats the purpose of generic interfaces. To make the inheritance chain really practical, you need to make is like this: generic interface => generic (possibly abstract) class implementing methods agnostic to generic parameters => set of complete implementation classes — using the idea shown in my previous answer. The term "class
" could be replaces with "struct
".
This is amazing: I just made a real "Freudian slip" (http://en.wikipedia.org/wiki/Freudian_slip[^])!
In the above paragraph, I wanted to type "(good) Microsoft naming conventions", but typed "goof Microsoft naming conventions"!
No, they are really good, not goof; and I''m using them.
[END EDIT #1]
After some thinking, I realized that I can see two more problems in the code above. I explained above, that 1) missing intermediate implementation class implementing just non-generic part of the interface; in this way you would not repeat implementation of those non-generic methods/properties in each of generic type-specific implementation. Two remaining problems are: 2) interfaces are designed to be use during run-time; remove all interfaces from your code — it will work in exact same way, why writing them? you should really use your classed via interfaces; 3) you use implicit interface implementation (via "public
"); but explicit interface implementation is much clearer.
My fix of the problems (2) and (3) required change in usage but then the use of interfaces is really justified in a practically beneficial way, and lack of code reuse if fixed (the schema you want is useful only when you need to have more than one class like DerivedSomething
, but if you try to write more classes you will see that MustStop
implementation is not reused); so, the code fixing all together will look like this:
// interfaces:
public interface IAgnostic {
bool MustStop { get; }
} //interface IAgnostic
public interface ISomething<T> : IAgnostic {
void DoSomething(T item);
} //ISomething<T>
//implementations:
abstract class Agnostic : IAgnostic {
bool IAgnostic.MustStop {
get { return true; }
} //MustStop
} //class Agnostic
class DerivedSomething : Agnostic, IAgnostic, ISomething<int> {
void ISomething<int>.DoSomething(int item) {
// Do stuff here, <T> is important for item.
} //DoSomething
//implementation of MustStop is reused
} //class DerivedSomething
class DerivedSomethingElse : Agnostic, IAgnostic, ISomething<int> {
void ISomething<int>.DoSomething(int item) {
// Do stuff here, <T> is important for item.
} //DoSomethingElse
//implementation of MustStop is again reused
} //class DerivedSomething
// usage:
class Test {
void TestMethod() {
DerivedSomething thing = new DerivedSomething();
//an extra variable somethigReferenced helps to
//avoid type case, which would be less safe:
ISomething<int> somethigReferenced = thing;
if (somethigReferenced.MustStop)
Console.WriteLine("Stop!");
else
Console.WriteLine("Don't stop!");
} //TestMethod
} //class Test
我修复了缺少代码重用的问题,这是通过附加接口IAgnostic
完成的,即与通用参数和附加抽象实现类IAgnostic
无关.通过使两个不同的终端完全实现类DerivedSomething
和DerivedSomethingElse
来演示此修复程序.
现在它是真的.
[END EDIT#2]
现在,我给您提供的解决方案甚至都没有触及统称为"协方差和逆方差"的根本主题.要真正理解这些问题,您确实需要学习本主题.在C#实践中,与以前的版本相比,主要是C#v.4中引入的新功能.
因此,请尝试阅读并理解以下内容:
http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29 [ ^ ],
http://blogs. msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx [ ^ ](您真的需要阅读其中的所有部分,但需要付出一些努力才能找到它们;此博客中的导航确实需要改进!),
http: //blogs.msdn.com/b/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-interface-variance.aspx [ ^ ],
http: //blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx [ ^ ].
只是为了让您了解在协方差/协方差问题中损失了多少C#和.NET:
I fix to the lack of code reuse is done via additional interface IAgnostic
that is, agnostic to generic parameters and additional abstract implementation class IAgnostic
. The fix is demonstrated by having two different terminal completely implementing classes, DerivedSomething
and DerivedSomethingElse
.
Now it''s really right.
[END EDIT #2]
Now, I gave you the resolution which does not even touch the root topic collectively named "covariance and contravariance". To really understand these issues you really need to learn this topic. In C# practice, it''s mostly about new features introduced to C# v.4 in comparison with prior versions.
So, try to read and understand this:
http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29[^],
http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx[^] (you really need to read all parts of it, but it needs some effort to find them; the navigation in this blog really needs improvement!),
http://blogs.msdn.com/b/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-interface-variance.aspx[^],
http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx[^].
Just to give you the idea how much C# and .NET are lost in covariance/contravariance issues:
//you can do this:
string[] strings = new string[] { "1", "2", };
object[] objectsobjects = strings;
//but you cannot do this, no matter with v.4 or prior version:
System.Collections.Generic.List<string> strList = new System.Collections.Generic.List<string>();
System.Collections.Generic.List<object> objList = strList; //with case or not
每当有一个使用其他类的实例并且使用和使用的类型是可派生的类时,协方差/相反方差问题就会起作用.因此,问题涉及数组,通用容器和通用委托.这超出了我们在此处讨论的强制转换主题.
The covariance/contravariance issues come into play each time there is a class using instances of other classes when using and used types are derivable. So, the issues are about arrays, generic containers and generic delegates. This goes beyond the casting topics we''re discussing here.
这篇关于有关强制转换泛型或“动态泛型"的问题,的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!