Reputation: 2803
I see that some properties allow beeing casted implicitly
. Like DataTable.Rows
cann be castet in DataRow
and in DateRowView
.
void DemoFunc()
{
DataTable dtTable = new DataTable();
foreach (DataRow row in dtTable.Rows)
{
}
foreach (DataRowView row in dtTable.Rows)
{
}
}
I could use this behaviour in my own code. Does anybody know a simple example to understand how it works?
Edit:
What is my goal? I wonder if its possible to design code like that:
Void DemoFunc2()
{
MyObject myObjec = new MyObject()
OtherObject otherObject = myObject.Property;
DifferentObject differentObject = myObject.Property;
}
Can a property return diffent object-types?
Edit 2:
In this question are mixed two different things. Once the wondering about how to get a property with two different return types, based on the missunderstandig on how DataTable.Rows is designed.
Upvotes: 0
Views: 83
Reputation: 1063198
Simple: that doesn't work:
Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'System.Data.DataRow' to type 'System.Data.DataRowView'.
However, foreach
works on a pattern that allows, in some cases, implicit casts. Whether or not they work. In this example, it cannot be checked by the compiler because .Rows
exposes an object
-based iterator (in this case, IEnumerable
/IEnumerator
, but even that is not strictly necessary).
More generally, there is the implicit conversion operator, but that still wouldn't apply with an object
-based iterator, because operators are applied against statically known types only (unless you use dynamic
).
In the scenario where the GetEnumerator()
method allows access to something more than object
, the compiler can tell us about this:
foreach (DataRowView row in dtTable.AsEnumerable())
{
}
to which the compiler adds an error:
Error 1 Cannot convert type 'System.Data.DataRow' to 'System.Data.DataRowView'
Although in this scenario, it would at least allow implicit operators to be used, if defined. Which they aren't.
More detail:
.Rows
is typed as DataRowCollection
. Since this dates back to .NET 1.1, it implements ICollection
and IEnumerable
, but it was never updated to implement IEnumerable<DataRow>
. Likewise, the GetEnumerator()
method returns IEnumerator
- not something more specific.
The problem with IEnumerator
(as different to IEnumerator<T>
) is that the .Current
is typed only as object
, because .NET 1.1 and C# 1.2 didn't have generics. It would have been very awkward if you had to do:
foreach(object tmp in table.Rows)
{
DataRowView row = (DataRowView)tmp; // this is our known-bad code
// ...
so for convenience, the language allows you to do that implicitly, i.e. the above is roughly identical to:
foreach(DataRowView row in table.Rows) // this is our known-bad code
{
// ...
What this actually becomes is something like:
{
IEnumerator iter = table.Rows.GetEnumerator();
// note this ^^^ is disposed after the loop if IDisposable; not shown
while(iter.MoveNext())
{
DataRowView row = (DataRowView) iter.Current; // .Current is: object
// ...
}
}
Notice how there is a cast from object
to DataRowView
? If the thing returned from .Current
is not actually a DataRowView
(and: it isn't), that will only fail at runtime, when the cast is performed. If we do the same thing with DataRow
instead of DataRowView
, it would have succeeded.
Note that the foreach
pattern doesn't actually depend on IEnumerator
; it is also possible for any type to have a GetEnumerator()
method, and return a custom iterator type that has a stronger (typed) .Current
implementation. If DataRowCollection
had done this, **your code with DataRowView
would fail. This could be a completely custom iterator, or could be simply IEnumerable<DataRow>
/ IEnumerator<DataRow>
. Consider the entirely hypothetical:
{
IEnumerator<DataRow> iter = table.Rows.GetEnumerator();
// note this ^^^ is disposed after the loop if IDisposable; not shown
while(iter.MoveNext())
{
DataRowView row = (DataRowView) iter.Current; // .Current is: DataRow
// ...
}
}
Now with this construct, we can see that we are casting from DataRow
to DataRowView
, and that is something that the compiler can check at compile-time. Since they are classes, it will fail unless there is either an inheritance relationship, or a custom conversion operator. Since neither exists: it will fail.
Upvotes: 3