Consider the following classes:
class A
{
public virtual string Name { get { return "A"; } }
}
class B : A
{
public override string Name { get { return "B"; } }
}
class C : A
{
public override string Name { get { return "C"; } }
}
And a list containing two objects of type B and one of type C:
List<A> l = new List<A>();
l.Add(new B());
l.Add(new C());
l.Add(new B());
Is there some way to force a check of the type at runtime and iterate only over the objects of type B in this list (short of writing a custom implementation of a list)? Something like:
foreach (B obj in l) // runtime error
Console.WriteLine(obj.Name);
// desired output:
// B
// B
I haven't run into this problem in a project, but the thought just occurred to me and I'm curious if this can be done. I'm aware the need for this feature could indicate a design flaw.
You can use .OfType<T>():
foreach (B obj in l.OfType<B>())
Console.WriteLine(obj.Name);
If you want only Bs, and not any subclasses of B to be listed, you can use the following:
foreach (B obj in l.Where(o => o.GetType() == typeof(B)))
Console.WriteLine(obj.Name);
foreach (A obj in l)
{
if (obj is B)
{
Console.WriteLine(obj.Name);
}
}
Related
Let's imagine that we have a class called A, and this class is inherited from class B, and also class B inherited from class C. We can extend this sequence as long as we want. How can we get all classes from this sequence? Like:
A
B
C
...
Try this code:
Type type = typeof(C);
while (type.BaseType!=null)
{
type = type.BaseType;
Console.WriteLine(type.Name);
}
// C
// B
// A
// Object
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
foreach (Type type in assembly.GetTypes())
{
if (typeof(A).IsAssignableFrom(type))
Console.WriteLine(type);
}
// These can be in various projects / assemblies
class A { }
class B : A { }
class C : A { }
class D : B { }
class E { }
class F : E { }
class G : D { }
Prints:
A
B
C
D
G
So, I have the following structure:
public abstract class MyBase
{
public Type TargetType { get; protected set; }
}
public class A : MyBase
{
public A()
{
TargetType = GetType();//Wrong, I need B class type not C
}
}
public class B : A
{
public B() { }
}
public class C : B
{
public C() { }
}
Of course, I can receive my type in this way:
public class B : A
{
public B()
{
TargetType = typeof(B);
}
}
Actually, I have to write some code to make the example clearer:
Class1.cs
public static Dictionary<Type, Type> MyTypes = new Dictionary<Type, Type>()
{
{ typeof(B),typeof(BView) }
}
public Class1()
{
C itemC = new C();
Class2.Initialize(itemC);
}
Class2.cs
public static Initialize(MyBase myBase)
{
Type t;
Class1.MyTypes.TryGetValue(myBase.TargetType, out t);
//I need get BView but I get null because *myBase.TargetType* is C class type
}
Level structure:
Level 0:(MyBase) - 1 object
Level 1:(A) - 2 objects
Level 2:(B) - 100 objects and more
Level 3:(C) - 80 objects and more
I gave this case in brackets
I will be grateful for any help
On any instance of an object you can call .GetType() to get the type of that object.
You don't need to set the type on construction
I didn't understand completely your question, but these are some possibilities to get informations about a type:
var a = new A();
Console.WriteLine(a.GetType().Name); // Output: A
Console.WriteLine(a.GetType().BaseType?.Name); // Output: MyBase
var b = new B();
Console.WriteLine(b.GetType().Name); // Output: B
Console.WriteLine(b.GetType().BaseType?.Name); // Output: A
// A simple loop to get to visit the derivance chain
var currentType = b.GetType();
while (currentType != typeof(object))
{
Console.WriteLine(currentType.Name);
currentType = currentType.BaseType;
}
// Output: B A MyBase
Also, I suggest to read this post about the difference between GetType and typeof
Hope this helps.
Is there a way to "mark" properties of an object so they will "stand out" in reflection?
For example:
class A
{
int aa, b;
string s1, s2;
public int AA
{
get { return aa; }
set { aa = value; }
}
public string S1
{
get { return s1; }
set { s1 = value; }
}
public string S2
{
get { return s2; }
set { s2 = value; }
}
}
class B : A
{
double cc, d;
C someOtherDataMember;
public C SomeOtherDataMember
{
get { return someOtherDataMember; }
}
public double CC
{
get { return cc; }
}
public double D
{
get { return d; }
set { d = value; }
}
}
class C
{...}
I want to be able to act on the data members of B only, i.e to mark them so I'll be able to tell them apart from the members of A.
Like this:
B b = new B();
var x = b.GetType().GetProperties();
foreach (PropertyInfo i in x)
{
if (/*property is marked*/)
{
Console.WriteLine(i.Name);
}
}
And it would be even better if it would be able to work without an instance of the object, e.g:
var x = B.GetType().GetProperties();
foreach (PropertyInfo i in x)
{
if (/*property is marked*/)
{
Console.WriteLine(i.Name);
}
}
Is it possible to do that?
I want to be able to act on the data members of B only, i.e to mark them so I'll be able to tell them apart from the members of A.
You can use custom attributes to add metadata to members, but it's not needed for what you want. You can use straight reflection. Look at DeclaringType, and use typeof(B) if you don't want to create an instance:
var x = typeof(B).GetProperties();
foreach (PropertyInfo i in x)
{
if (i.DeclaringType == typeof(B))
{
Console.WriteLine(i.Name);
}
}
you can also apply that filter when getting the properties:
var x = typeof(B).GetProperties(BindingFlags.DeclaredOnly
| BindingFlags.Public
| BindingFlags.Instance);
foreach (PropertyInfo i in x)
{
Console.WriteLine(i.Name);
}
You can write your custom attributes, and then using GetCustomAttributes method you can check the properties.
GetCustomAttributes
For example
foreach (PropertyInfo property in properties)
{
var ca = property.GetCustomAttributes(false);
foreach (var attribute in ca)
{
if (attribute is YourAttribute)
{
//...
}
}
}
I'm a complete newbie to C# so excuse me if this looks weird.
I have an abstract class called vefHlutir
namespace Klasasafn
{
public abstract class vefHlutur
{
public abstract List<String> columnNames();
public abstract List<String> toStringList();
}
}
//Here is an object that inherits from this abstract class:
namespace Klasasafn
{
[Table(Name="Users")]
public class User: vefHlutur
{
public override List<String> columnNames()
{
List<String> p = new List<String>();
p.Add("Nafn");
p.Add("Email");
p.Add("Lýsing");
return p;
}
public override List<String> toStringList()
{
List<String> p = new List<String>();
p.Add(name);
p.Add(email);
p.Add(descr);
return p;
}
... more stuff here
}
}
//And here is the code I'm trying to run, Item, User and Category all inherit from vefHlutir:
List<Klasasafn.Item> hlutir;
List<Klasasafn.User> notendur;
List<Klasasafn.Category> flokkar;
void Page_Init(object sender, EventArgs e)
{
hlutir = Fac.getItemList();
notendur = Fac.getUserList();
flokkar = Fac.getCategoryList();
prenta(notendur, Table1);
}
protected void Page_Load(object sender, EventArgs e)
{
}
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
if (DropDownList1.SelectedIndex == 0)
{
prenta(notendur, Table1);
}
else if (DropDownList1.SelectedIndex == 1)
{
prenta(hlutir, Table1);
}
else
prenta(flokkar, Table1);
}
private void prenta(List<vefHlutur> foo, Table f)
{
List<String> columnNames = foo[0].columnNames();
TableRow tRow1 = new TableRow();
f.Rows.Add(tRow1);
foreach (String i in columnNames)
{
TableCell columnNameCell = new TableCell();
tRow1.Cells.Add(columnNameCell);
columnNameCell.Controls.Add(new LiteralControl(i));
}
foreach (vefHlutur j in foo)
{
TableRow tRow = new TableRow();
f.Rows.Add(tRow);
List<String> töfluHlutir = j.toStringList();
foreach (String k in töfluHlutir)
{
TableCell tCell1 = new TableCell();
tRow.Cells.Add(tCell1);
tCell1.Controls.Add(new LiteralControl(k));
}
}
}
My problem is that I can't use the method prenta.
I always get these errors:
Error 1 The best overloaded method match for 'Forsíða.prenta(System.Collections.Generic.List, System.Web.UI.WebControls.Table)' has some invalid arguments
Error 2 Argument '1': cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.List
How do I solve this?
The problem is that in C#, the type List<ChildClass> cannot be used when the method is typed for List<ParentClass>. This type of conversion is known as covariance and it will not be available in C# until 4.0 and then only on interfaces and events.
What you can do though is make the method generic and add a constraint.
private void prenta<T>(List<T> foo, Table f)
where T : vefHlutur
{
...
}
What this code is doing is saying that prenta will accept a List<T> as the first parameter for any case where T is or derivecs from the type vefHlutur. It also allows you to treat the type T as if it is the type vefHlutur with respect to calling methods, properties, etc ... This should allow your scenario to work.
There is a way to do the cast. A little unsafe code! Don't be afraid of this post. Its mostly test code to show that it works. All the work happens here:
static unsafe List<A> CastBasAIL(List<B> bIn) {
DynamicMethod dynamicMethod = new DynamicMethod("foo1", typeof(List<A>),
new[] { typeof(List<B>) }, typeof(void));
ILGenerator il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // copy first argument to stack
il.Emit(OpCodes.Ret); // return the item on the stack
CCastDelegate HopeThisWorks = (CCastDelegate)dynamicMethod.CreateDelegate(
typeof(CCastDelegate));
return HopeThisWorks(bIn);
}
This solution works as long as the thing you are trying to cast has the same instance field layout as the thing you are casting it to (inheritance situations work well). Note, there are some things that will give you type mismatch errors: i.e. if the List attempts to create a base type in a covariant situation. Just test after doing this.
I apologize to purists for this, but I am a recovering c/c++vb/aseembly programmer!
namespace Covariant {
class A {
public virtual string Name() { return "A"; }
}
class B : A {
public override string Name() { return "B"; }
}
delegate List<A> CCastDelegate(List<B> b); // be used in the cast
class Program {
static unsafe List<A> CastBasAIL(List<B> bIn) {
DynamicMethod dynamicMethod = new DynamicMethod("foo1", typeof(List<A>), new[] { typeof(List<B>) }, typeof(void));
ILGenerator il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // copy first argument to stack
il.Emit(OpCodes.Ret); // return the item on the stack
CCastDelegate HopeThisWorks = (CCastDelegate)dynamicMethod.CreateDelegate(typeof(CCastDelegate));
return HopeThisWorks(bIn);
}
static void Main(string[] args) {
// make a list<B>
List<B> b = new List<B>();
b.Add(new B());
b.Add(new B());
// set list<A> = the list b using the covariant work around
List<A> a = CastBasAIL(b);
// at this point the debugger is miffed with a, but code exectuing methods of a work just fine.
// It may be that the debugger simply checks that type of the generic argument matches the
// signature of the type, or it may be that something is really screwed up. Nothing ever crashes.
// prove the cast really worked
TestA(a);
return;
}
static void TestA(List<A> a) {
Console.WriteLine("Input type: {0}", typeof(List<A>).ToString());
Console.WriteLine("Passed in type: {0}\n", a.GetType().ToString());
// Prove that A is B
Console.WriteLine("Count = {0}", a.Count);
Console.WriteLine("Item.Name = {0}", a[0].Name());
// see if more complicated methods of List<A> still work
int i = a.FindIndex(delegate(A item) { return item.Name() == "A"; });
Console.WriteLine("Index of first A in List<A> = {0}", i);
i = a.FindIndex(delegate(A item) { return item.Name() == "B"; });
Console.WriteLine("Index of first B in List<A> = {0}\n", i);
// can we convert a to an array still?
Console.WriteLine("Iterate through a, after converting a to an array");
foreach (var x in a.ToArray())
Console.WriteLine("{0}", x.Name());
}
}
}
public class Item
{
private int _rowID;
private Guid _itemGUID;
public Item() { }
public int Rid
{
get
{
return _rowID;
}
set { }
}
public Guid IetmGuid
{
get
{
return _itemGuid;
}
set
{
_itemGuid= value;
}
}
}
The above is my custom object.
I have a list:
List<V> myList = someMethod;
where V is of type Item, my object.
I want to iterate and get the properties as such
foreach(V element in mylist)
{
Guid test = element.IetmGuid;
}
When I debug and look at the 'element' object I can see all the properties in the 'Quickwatch' but I cannot do element.IetmGuid.
Are you putting a constraint on the generic type V? You'll need to tell the runtime that V can be any type that is a subtype of your Item type.
public class MyGenericClass<V>
where V : Item //This is a constraint that requires type V to be an Item (or subtype)
{
public void DoSomething()
{
List<V> myList = someMethod();
foreach (V element in myList)
{
//This will now work because you've constrained the generic type V
Guid test = element.IetmGuid;
}
}
}
Note, it only makes sense to use a generic class in this manner if you need to support multiple kinds of Items (represented by subtypes of Item).
Try declaring your list like this:
List<Item> myList = someMethod;
foreach( object element in myList ) {
Item itm = element as Item;
if ( null == itm ) { continue; }
Guid test = itm.ItemGuid;
}
Your list should be declared like this:
List<V> myList = someMethod;
Where V is the type item.
and then your iteration was correct:
foreach(V element in myList)
{
Guid test = element.IetmGuid;
}