Reputation: 4827
I have a class like this:
public class ItemField
{
public string FieldName { get; set; }
public object FieldValue
{
get;
set;
}
}
I want to have it so that "FieldValue" can only be of type string, MyItem, or List<MyItem>. I also want it so that when code consumes this class, accessing "FieldValue" returns the right type of object. Is there a way to do this? I'm not too clear on generics, but there seems to be a way to do this using that...
Upvotes: 1
Views: 430
Reputation: 75306
As you know string
is not used as generic constraint, take a look on MSDN:
public sealed class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string>
So you can work around like this if you to constraint T as string
public class ItemField<T> where T : ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string>
{
public string FieldName { get; set; }
public T FieldValue { get; set; }
}
Upvotes: 1
Reputation: 203821
So it seems that you're not sure if you should be returning one item or a collection of items. Rather than changing the return type dynamically (which is something you can't really do in a useful way) it would make more sense to just always return a collection. That collection can always contain a single item if that's appropriate.
If you change the return type to List<MyItem>
then most of the problems and bad code smell just go away.
Upvotes: 1
Reputation: 726489
Assuming that the client knows exactly what type of the field it gets, you can change the property definition into a function to use generics, like this:
private object fieldValue;
public T GetFieldValue<T>() {
return (T)fieldValue;
}
The users must call this method like this:
MyItem item = itemField.GetFieldValue<MyItem>();
However, the cast is still there, and it is not clear how the users are expected to know what type to supply as the generic type parameter.
A better approach would be to use some sort of a double dispatch mechanism, for example, the visitor pattern, to "invert" the direction and let your field "push" the value into the caller.
interface IItemFieldVisitor {
void VisitString(string val);
void VisitMyItem(MyItem val);
void VisitList(IList<MyItem> val);
}
interface IItemField {
string Name {get;set;}
void Accept(IItemFieldVisitor visitor);
}
class StringItemField : IItemField {
public string Name {get;set;}
private string val;
public void Accept(IItemFieldVisitor visitor) {
visitor.VisitString(val);
}
}
class MyItemItemField : IItemField {
public string Name {get;set;}
private MyItem val;
public void Accept(IItemFieldVisitor visitor) {
visitor.VisitMyItem(val);
}
}
class MyItemListItemField : IItemField {
public string Name {get;set;}
private IList<MyItem> val;
public void Accept(IItemFieldVisitor visitor) {
visitor.VisitList(val);
}
}
Now you can create instances of IItemFieldVisitor
interface, and pass them to IItemField
objects. The visitors will receive callbacks with specific values without knowing the type of the IItemField
objects being visited.
Upvotes: 2
Reputation: 1497
you could create your own abstract class 'Field' and have a string, MyItem or List derive from the abstract class. http://www.techotopia.com/index.php/Understanding_C_Sharp_Abstract_Classes
you can look into abstract class factory at this link: http://dotnetslackers.com/articles/designpatterns/Design-Patterns-Part-3.aspx
Upvotes: 1
Reputation: 700212
A property can only return one specific type, you can't make it return different data types depending on what the actual type of the field value is.
You can use generics for the class, but then you need to create the class instance for the type of data that you want to store in it:
public class ItemField<T> {
public string FieldName { get; set; }
public T FieldValue {
get;
set;
}
}
Upvotes: 2