I am trying to find a property of a class in a generic method.
Class abc
-property Name : xyz
I have a method like :
public bool findItem<T> (IEnumerable<T> items) where T : Data
{
if (typeof(T) == typeof(abc))
{
// Code comes inside the loop here
// how to find something like items.First().xyz
}
}
I want to find the property value for the first item but it doesnt work (there is no autosuggest too).
While debugging (in immediate window), I can see my object with class Name when i do items.First().
I tried items.First()["xyz"] but doesnt work. (Cannot apply indexing with [] to an expression of type T)
You certainly could do something like this:
public bool FindItem<T>(IEnumerable<T> items)
{
if (items is IEnumerable<Abc> abcs)
{
int first = abcs.First().Xyz;
return true;
}
else if (items is IEnumerable<Def> defs)
{
int first = defs.First().Ghi;
return true;
}
return false;
}
However, that's not the normal way to do it. Normally you'd just use overloads:
public bool FindItem(IEnumerable<Abc> items)
{
int first = items.First().Xyz;
return true;
}
public bool FindItem(IEnumerable<Def> items)
{
int first = items.First().Ghi;
return true;
}
Related
I have inherited a VB application and am looking for converting some things to C# (part of migration). I have an extension method in VB that is like this:
<Extension()>
Public Function ContainsAnyOf(Of T)(SourceList As List(Of T), ParamArray ParamList() As T) As Boolean
If SourceList IsNot Nothing AndAlso ParamList IsNot Nothing Then
For Each ParamItem As T In ParamList
If SourceList.Contains(ParamItem) Then Return True
Next
End If
Return False
End Function
I have translated it to C# as follows:
public static bool ContainsAnyOf<T>(this List<T> sourceList, IEnumerable<T> parameters)
{
if (parameters.Count() == 0 || sourceList == null || sourceList.Count < 1)
{
return false;
}
else
{
foreach (var parameter in parameters)
{
if (sourceList.Contains(parameter))
return true;
else
return false;
}
return false;
}
}
To be complete, the model is declared in a separate cs-file as follows:
public class TestModel
{
public int Id { get; set; }
public string ModelValue1 { get; set; }
public string ModelValue2 { get; set; }
public bool ModelBool { get; set; }
}
In a testapplication (console) I have following testcode:
var testAnyOf = false;
var testElement = new List<string>(){ "Test 2", "Testje 2" };
if (model.ContainsAnyOf(testElement))
{
testAnyOf = true;
}
Console.WriteLine($"Does the list contains the {testElement}? Outcome = {testAnyOf}");
Console.ReadLine();
But I get a compiler error CS0411: The type arguments for method 'ListExtensions.ContainsAnyOf(List, IEnumerable)' canot be inferred from the usage ...
I know that what the Error means but I have tried few things now but still don't know how to pass the 'Parameters'.
Eventually what I need is like in the original VB application and that works like:
bool isInList = alistOfModel.ContainsAnyOf("One", "Dog", "Hat")
I think the first part with the List of type T is correct but how to pass the parameters..
Thanks!
That should compile if model is of type List<string>. I assume that model is of some other type.
However, I would implement this using Linq like so (likely to be much more performant):
public static bool ContainsAnyOf<T>(this IEnumerable<T> sourceList, IEnumerable<T> parameters)
{
return sourceList.Intersect(parameters).Any();
}
You could also use params so that you can call it with an argument list of items to match:
public static bool ContainsAnyOf<T>(this IEnumerable<T> sourceList, params T[] parameters)
{
return sourceList.Intersect(parameters).Any();
}
Using the latter, instead of writing
var testElement = new List<string>(){ "Test 2", "Testje 2" };
if (model.ContainsAnyOf(testElement))
You could write
if (model.ContainsAnyOf("Test 2", "Testje 2"))
If you want a direct translation of that VB code then this is it:
public bool ContainsAnyOf<T>(this List<T> SourceList, params T[] ParamList)
{
if (SourceList != null && ParamList != null)
{
foreach (T ParamItem in ParamList)
{
if (SourceList.Contains(ParamItem))
return true;
}
}
return false;
}
If you're going to be translating VB to C# then I suggest that you download and install Instant C# from Tangible Software Solutions.
BTW, there's never any point to null-checking a parameter declared params/ParamArray as they will always have a value. They may be empty but the array will always exist.
I have created a class which inherits from List: Now I want to iterate the elements of the class in the class itself and return the object if it satisfies certain criteria (e.g. its Property name):
protected class myList : List<Object>
{
getElement(string name)
{
??? LOOP AND IF CONDITION ???
return element;
}
}
Can you help? Thanks
You're looking for foreach(var item in this).
First, I would try to avoid using List<Object>.The problem with Object is, that it needs casting. Hence it is prone to errors at runtime.
Rather create your own class.
For example, a simple item, which holds a name.
class Item
{
public string Name { get; set; }
}
Then you can simply do:
class myList : List<Item>
{
Item getItem(string name)
{
foreach(var item in this)
{
if(item.Name==name) { return item; }
}
return null;
}
}
You'd want some more code for the casting, but:
public Object GetElement(string name)
{
Object element = null;
foreach (var item in this)
{
if (item.ToString() == name)
{
element = item;
break;
}
}
return element;
}
You don't have to reinvent the wheel, just use LINQ.
mylist.First(element => /*criteria*/);
I have a custom class called CustomClass. It contains a variable called "Name" and a list of values (for the sake of simplicity let's make this an int - in reality it is another custom class, but the principle should be the same).
So :
public class CustomClass {
string name;
}
I have a List<CustomClass>.
When I attempt to add a value to this List, the logic I want, is for this List to check if it contains a CustomClass with the same name as the value I want to add.
If it does, then do x, otherwise, do y.
listOfCustomClass.Contains(customClassToAdd.name) will not work in this case, I assume, however this is the functionality I require.
What is best practice here ?
I think you can try something like var x = MyList.Where(C=> C.Name == InsertedName) and check the result (not tested)
You'll have to create a new class,let's call it CustomList, that inherits from IList<> where you can override the add method, do your check, and then add it to the base. Something like this:
public class CustomList<T> : IList<T> where T : CustomClass
{
private List<T> innerlist;
public void Add(T item)
{
if(innerlist.Any(a => a.Name == item.Name)))
innerlist.Add(item);
}
}
you can do it using linq as follow but you have to make name field public.
List<CustomClass> list = new List<CustomClass>();
CustomClass toCheck = new CustomClass();
if (list.Any(p => p.name.Equals(toCheck)))
{
//do x here
}
else
{
//do y here
}
however if you don't want to use linq then Do some changes in CustomClass as follow
public class CustomClass
{
string name;
List<int> intLost = new List<int>();
public override bool Equals(object obj)
{
return this.Equals(obj as CustomClass);
}
public override int GetHashCode()
{
return 0;
}
public bool Equals(CustomClass cc)
{
if (cc == null) return false;
return this.name.Equals(cc.name);
}
}
Then you can do this.
List<CustomClass> list = new List<CustomClass>();
CustomClass toCheck = new CustomClass();
if (list.Contains(toCheck))
{
//do x here
}
else
{
//do y here
}
It seems to me that you want to override the .Add() behavior of your List<CustomClass>. While you could use extension methods, I think a better solution would be to invent a class that extends List in some manner. I'd recommend implementing IList in your collection class if you need to have that level of control over add operations...
public class CustomClassList : IList<CustomClass>
{
public void Add (CustomClass item)
{
if(this.Select(t => t.Name).Contains(item.Name))
// Do whatever here...
else
// Do whatever else here...
}
// ... other IList implementations here ...
}
try this:
IList<CustomClass> list = new List<CustomClass>();
CustomClass customClass = new CustomClass();
customClass.name = "Lucas";
if((list.Tolist().Find(x => x.name == customClass.name)) == null)
{
list.Add(customClass);
}
else
{
//do y;
}
You could override the Equals(object o) function in your CustomClass, so that two CustomClasses are considered equal if their names are the same. Then
listOfCustomClass.Contains(customClassToAdd);
should work.
Another way is to override Equals method on your CustomClass and then just call List.Contains()
If the name property uniquely identifies the CustomClass, then you should overload Equals and GetHashCode(). The reason List.Contains doesn't work is that underneath the HashCodes are compared. So you need to overload GetHashCode and Equals something like this:
public override int GetHashCode()
{
return this.name.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as CustomClass;
if (other != null)
{
if (other.Name == this.Name)
{
return true;
}
}
return false;
}
I've lots of enums in my app. Most of them are used on combos like this:
Enum.GetValues(typeof(TipoControlador))
Now I'd like to localize them like this: Localizing enum descriptions attributes
How can I combine them? My first thought was to override the ToString method with an extension method, but that's not possible =(
Using the other article as a basis, you can create an extension method like this:
public static class LocalizedEnumExtensions
{
private static ResourceManager _resources = new ResourceManager("MyClass.myResources",
System.Reflection.Assembly.GetExecutingAssembly());
public static IEnumerable<string> GetLocalizedNames(this IEnumerable enumValues)
{
foreach(var e in enumValues)
{
string localizedDescription = _resources.GetString(String.Format("{0}.{1}", e.GetType(), e));
if(String.IsNullOrEmpty(localizedDescription))
{
yield return e.ToString();
}
else
{
yield return localizedDescription;
}
}
}
}
You would use it like this:
Enum.GetValues(typeof(TipoControlador)).GetLocalizedNames();
Technically, this extension method will accept any array, and you can't restrict it to only work on an enum, but you could add extra validation inside the extension method if you feel it's important:
if(!e.GetType().IsEnum) throw new InvalidOperationException(String.Format("{0} is not a valid Enum!", e.GetType()));
You have 2 problems here, the first is how to localize enums which is solved by Localizing enum descriptions attributes.
The second is how to display the localized name whilst using the enum's value. This can be solved by creating a simple wrapper object such as:
public sealed class NamedItem
{
private readonly string name;
private readonly object value;
public NamedItem (string name, object value)
{
this.name = name;
this.value = value;
}
public string Name { get { return name; } }
public object Value { get { return value; } }
public override string ToString ()
{
return name;
}
}
This provides a generic re-usable class for any drop down box where you might want to show a different name for an item than the item itself provides (eg enums, ints, etc).
Once you have this class, you can set the drop down's DisplayMember to Name and ValueMember to Value. This will mean that dropdown.SelectedValue will still return your enum.
I know this question is old, but this may help some people.
You can just handle the Format event of the ComboBox control (http://msdn.microsoft.com/en-us/library/system.windows.forms.listcontrol.format.aspx), and add your text logic in it.
private void ComboBoxFormat(object sender, ListControlConvertEventArgs e)
{
e.Value = GetDescription(e.Value);
}
public static string GetDescription(object item)
{
string desc = null;
Type type = item.GetType();
MemberInfo[] memInfo = type.GetMember(item.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
desc = (attrs[0] as DescriptionAttribute).Description;
}
}
if (desc == null) // Description not found
{
desc = item.ToString();
}
return desc;
}
With this, the ComboBox control still holds enum values rather than strings.
I'm not entirely sure if I have all the terminology correct so forgive me if I'm wrong. I was wondering if it would be possible to send an argument(s) to the method. Take the following for example.
public item (int index)
{
get { return list[index]; }
set { list[index] = value; }
}
I know that as it is, it will error. What I'm asking is if there is some way to get it working. Any suggestions or should I figure out some way around it?
Thanks in advance.
Try this:
// This returns an instance of type "Foo",
// since I didn't know the type of "list".
// Obviously the return type would need to
// match the type of whatever "list" contains.
public Foo this[int index]
{
get { return list[index]; }
set { list[index] = value; }
}
This is C#'s indexer syntax and it has some limitations (it's not as flexible as VB.NET's parameterful properties) but it does work for your specific example.
As others have shown, you can turn it into an indexer - which can have multiple parameters, by the way.
What you can't do is name an indexer in C#... although you can in VB. So you can't have two indexers, one called Foo and the other called Bar... you'd need to write properties which returned values which were themselves indexable. It's a bit of a pain, to be honest :(
This is called indexer property
public int this [int index]
{
get { return list[index]; }
set { list[index] = value; }
}
I think what you might be looking for is:
public Something this[int index]
{
get
{
return list[index];
}
set
{
list[index] = value;
}
}
For the record, Whilst the other answers are valid, you might also want to consider using the following approach:
public IList<Something> items { get; set; }
This could then be used as follows:
Something item = myFoo.items[1];
The other answers would be used in the following, slightly different, way:
Something item = myFoo[1];
The one you want depends on what exactly you are trying to achieve, which is difficult to determine without seeing the rest of the code.
Besides the indexer that has been mentioned several times now, another possibility is to make a custom class with an indexer and return an instance of it as a property.
Example:
public class IntList
{
public IntList(IEnumerable<int> source)
{
items = source.ToArray();
Squares = new SquareList(this);
}
private int[] items;
// The indexer everyone else mentioned
public int this[int index]
{
get { return items[index]; }
set { items[index] = value; }
}
// Other properties might be useful:
public SquareList Squares { get; private set; }
public class SquareList
{
public SquareList(IntList list)
{
this.list = list;
}
private IntList list;
public int this[int index]
{
get { return list.items[index] * list.items[index]; }
}
}
}
You can use indexator for solving this problem
public object this[string name]
{
get
{
int idx = FindParam(name);
if (idx != -1)
return _params[idx].Value;
throw new IndexOutOfRangeException(String.Format("Parameter \"{0}\" not found in this collection", name));
}
set
{
int idx = FindParam(name);
if (idx != -1)
_params[idx].Value = value;
else
throw new IndexOutOfRangeException(String.Format("Parameter \"{0}\" not found in this collection", name));
}
}