displaying structs in an array using enumerator - c#

In an object I have :
public IEnumerable<voiture> recup_voitures()
{
foreach (voiture v in _arrVCollection)
{
yield return (v);
}
}
voiture being a struct and _arrVCollection being an array containing some struct voiture.
In my main class I have :
foreach (CarCollection.voiture o in collection.recup_voitures())
{
//some code to display the content of each struct
}
What is happening is that if I have an array of length 5 and only 1 struct voiture in it, it will do the displaying code 5 times instead of only 1. What am I doing wrong?

If you are creating array of struct with length, say 5, it means array will contain 5 structs constructed using default constructor.
voiture[] arr = new voiture[5];
arr[0] = some_non-default_voiture;
In that case your array will contain one non-default voiture instance and four default instances.
To get only one voiture instance you can create array with length 1, or use List<T>:
List<voiture> = new List<voiture> { new voiture() };
If you really need to deal with array with length 5, consider to use nullable types:
voiture?[] _arrVCollection = new voiture?[5];
_arrVCollection[0] = new voiture();
And enumerate like this:
public IEnumerable<voiture> recup_voitures()
{
foreach (voiture? v in _arrVCollection)
{ if (v.HasValue)
yield return v.Value;
}
}
Edit:
As Servy mentioned, method recup_voitures() can be rewritten with linq:
public IEnumerable<voiture> recup_voitures()
{
return _arrVCollection.Where(x => x.HasValue).Select(x => x.Value);
}

In your current code you get exception.
You can change your code in recup_voitures methods if the voiture will be class:
public static IEnumerable<voiture> recup_voitures()
{
foreach (voiture v in _arrVCollection)
{
if (v != null)
yield return (v);
}
}

Related

Convert type List<object> to 'T'

I'm trying to write a generic function:
private T Convert<T>(List<object> list) where T : new()
{
if (typeof(T).IsArray)
{
T array = new T();
// ... convert here
return array;
}
}
In this scenario, I know that the objects in list should be the same type as the array of T. For example, if T is System.Int32[] then I'd expect the objects in list to all be Int32.
However, T is not always an array. In other cases, it may be a value type or even some other reference type.
How can I convert this List<object> to T given these assumptions?
UPDATE
This seems to be working within a set of given assumptions:
private T Convert<T>(List<object> list)
{
// ... code omitted for brevity
if (typeof(T).IsArray)
{
// big assumption here
Type elementType = Type.GetType(typeof(T).FullName.Replace("[]", string.Empty));
Array array = Array.CreateInstance(elementType, list.Count);
for (int i = 0; i < list.Count; i++)
{
array.SetValue(list[i], i);
}
return (T)Convert.ChangeType(array, typeof(T));
}
}
You can use the ancient ArrayList to create an array of the desired type. (It's pretty much the only thing it is good for.)
private T Convert<T>(List<object> list)
{
if (typeof(T).IsArray)
{
var tmp = new ArrayList(list);
return (T)(object)tmp.ToArray(typeof(T).GetElementType());
}
return (T)list.First();
}
Fiddle
Note: You should probably add some error handling, e.g. if the caller specified a T that doesn't make sense for the list.

Detect if an object is a ValueTuple

I have a use case where I need to check if a value is a C# 7 ValueTuple, and if so, loop through each of the items. I tried checking with obj is ValueTuple and obj is (object, object) but both of those return false. I found that I could use obj.GetType().Name and check if it starts with "ValueTuple" but that seems lame to me. Any alternatives would be welcomed.
I also have the issue of getting each item. I attempted to get Item1 with the solution found here: How do I check if a property exists on a dynamic anonymous type in c#? but ((dynamic)obj).GetType().GetProperty("Item1") returns null. My hope was that I could then do a while to get each item. But this does not work. How can I get each item?
Update - more code
if (item is ValueTuple) //this does not work, but I can do a GetType and check the name
{
object tupleValue;
int nth = 1;
while ((tupleValue = ((dynamic)item).GetType().GetProperty($"Item{nth}")) != null && //this does not work
nth <= 8)
{
nth++;
//Do stuff
}
}
Structures do not inherit in C#, so ValueTuple<T1>, ValueTuple<T1,T2>, ValueTuple<T1,T2,T3> and so on are distinct types that do not inherit from ValueTuple as their base. Hence, obj is ValueTuple check fails.
If you are looking for ValueTuple with arbitrary type arguments, you can check if the class is ValueTuple<,...,> as follows:
private static readonly Set<Type> ValTupleTypes = new HashSet<Type>(
new Type[] { typeof(ValueTuple<>), typeof(ValueTuple<,>),
typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>), typeof(ValueTuple<,,,,,,,>)
}
);
static bool IsValueTuple2(object obj) {
var type = obj.GetType();
return type.IsGenericType
&& ValTupleTypes.Contains(type.GetGenericTypeDefinition());
}
To get sub-items based on the type you could use an approach that is not particularly fast, but should do the trick:
static readonly IDictionary<Type,Func<object,object[]>> GetItems = new Dictionary<Type,Func<object,object[]>> {
[typeof(ValueTuple<>)] = o => new object[] {((dynamic)o).Item1}
, [typeof(ValueTuple<,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2}
, [typeof(ValueTuple<,,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2, ((dynamic)o).Item3}
, ...
};
This would let you do this:
object[] items = null;
var type = obj.GetType();
if (type.IsGeneric && GetItems.TryGetValue(type.GetGenericTypeDefinition(), out var itemGetter)) {
items = itemGetter(obj);
}
Regarding the part of the question "How can I get each item?"...
Both ValueTuple and Tuple both implement ITuple, which has a length property and an indexer property. So a the following console app code lists the values to the console:
// SUT (as a local function)
IEnumerable<object> GetValuesFromTuple(System.Runtime.CompilerServices.ITuple tuple)
{
for (var i = 0; i < tuple.Length; i++)
yield return tuple[i];
}
// arrange
var valueTuple = (StringProp: "abc", IntProp: 123, BoolProp: false, GuidProp: Guid.Empty);
// act
var values = GetValuesFromTuple(valueTuple);
// assert (to console)
Console.WriteLine($"Values = '{values.Count()}'");
foreach (var value in values)
{
Console.WriteLine($"Value = '{value}'");
}
Console output:
Values = '4'
Value = 'abc'
Value = '123'
Value = 'False'
Value = '00000000-0000-0000-0000-000000000000'
This is my solution to the problem. A PCL compatible extension class. Special thanks to #dasblinkenlight and #Evk for helping me out!
public static class TupleExtensions
{
private static readonly HashSet<Type> ValueTupleTypes = new HashSet<Type>(new Type[]
{
typeof(ValueTuple<>),
typeof(ValueTuple<,>),
typeof(ValueTuple<,,>),
typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>),
typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>),
typeof(ValueTuple<,,,,,,,>)
});
public static bool IsValueTuple(this object obj) => IsValueTupleType(obj.GetType());
public static bool IsValueTupleType(this Type type)
{
return type.GetTypeInfo().IsGenericType && ValueTupleTypes.Contains(type.GetGenericTypeDefinition());
}
public static List<object> GetValueTupleItemObjects(this object tuple) => GetValueTupleItemFields(tuple.GetType()).Select(f => f.GetValue(tuple)).ToList();
public static List<Type> GetValueTupleItemTypes(this Type tupleType) => GetValueTupleItemFields(tupleType).Select(f => f.FieldType).ToList();
public static List<FieldInfo> GetValueTupleItemFields(this Type tupleType)
{
var items = new List<FieldInfo>();
FieldInfo field;
int nth = 1;
while ((field = tupleType.GetRuntimeField($"Item{nth}")) != null)
{
nth++;
items.Add(field);
}
return items;
}
}
hackish one liner
type.Name.StartsWith("ValueTuple`")
(can be extended to check the digit at the end)

Alternative to using ref in foreach?

I have a modifying method with a signature like
private bool Modify(ref MyClass obj);
that will make modifications to obj and indicate succes with it's return value. Modify is not reassigning the reference (I know that this wouldn't work), just modifying instance fields, so I want to use it to do something like the following:
foreach(MyClass obj in myList)
{
bool success = Modify(obj);
// do things depending on success
}
I am running into a problem compiling as obj is "not being passed with the ref keyword". However, if I put the ref keyword in like so:
bool success = Modify(ref obj);
I get "cannot use obj as a ref/out because it is a 'foreach iteration variable". I understand that foreach uses an immutable iterator and that's why this doesn't work.
My question is what is the easiest alternative to make something like this work?
I have tried using
foreach(int i = 0; i < myList.Count; i++)
{
bool success = Modify(ref myList[i]);
// do things depending on success
}
but they I get "a property or indexer may not be passed as an out of ref parameter".
Thanks your help.
Any type within C# is passed actually by value. When you pass an instance of a class to a method what is actually passed is not the instance itself but a reference to it which itself is passed by value. So effectivly you're passing instances of a class as reference - which is why you call them reference-types.
In your case you just modify an existing instance referenced by that reference-value in your method, no need to use the ref-keyword.
foreach(var m in myList)
{
MyMethod(m);
}
MyMethod(MyClass instance)
{
instance.MyProperty = ...
}
If you'd really pass the reference by reference you'd re-assign the obj on every iteration within your loop which isn't allowed within a foreach-block. This would be similar to the following:
foreach(var m in myList)
{
m = new MyClass();
}
On the other side you could also use a classic for-loop. However you'd need a temporary variable to store the outcome of your method:
for(int i = 0; i < myList.Length; i++)
{
var tmp = myList[i];
MyMethod(ref tmp);
myList[i] = tmp;
}
You state
Modify is not reassigning the reference
Therefore, there is no reason the Modify(ref MyClass) function needs to pass argument by ref.
You should be able to do the same "modifications", whatever that is (please clarify that) by passing the object reference by value, i.e. removing the ref keyword.
So, the fix here should be changing your Modify function signature from Modify(ref MyClass) to Modify(MyClass)
use a temp variable to bypass the message
foreach(MyClass obj in myList)
{
MyClass objTemp = obj;
bool success = Modify(ref objTemp);
// do things depending on success
}
private MyMethod(ref MyClass instance)
{
instance.MyProperty = ...
}
It is solved by using LINQ.
My Code:
private static List<string> _test = new List<string>();
public List<string> Test { get => _test; set => _test = value; }
static void Main(string[] args)
{
string numString = "abcd";
_test.Add("ABcd");
_test.Add("bsgd");
string result = _test.Where(a => a.ToUpper().Equals(numString.ToUpper()) == true).FirstOrDefault();
Console.WriteLine(result + " linq");
}
You need to cast your array to a Span:
int[] nums = new[] { 1, 2, 3, 4, 5 };
int i = 0;
foreach (ref var item in nums.AsSpan())
{
item += 10;
Console.WriteLine(nums[i++]);
}
output:
11
12
13
14
15
You can modify the iteration variable because Span Enumerator's Current property is of ref T type.

Function that returns different types?

Suppose I have a container class that contains two properties:
string contents
bool isCommaSeperated
And a function:
? compute()
{
if(this.isCommaSeperated)
{
return contents.split(",").toList();
}
else
{
return contents;
}
}
Is there a way for this function to return either a string or a list of strings?
Or what type of design would allow me to achieve something similar?
I would just return both results as a collection:
IList<string> compute()
{
if (this.isCommaSeperated)
{
return contents.Split(","); // No need to turn the array into a list
}
return new[] { contents };
}
You can use dynamic to implement that:
dynamic Compute(bool isCommaSeperated)
{
if(isCommaSeperated)
{
return contents.Split(",").ToList();
}
else
{
return contents;
}
}
Code will still preserve type information, but let you return any type you like and fail at run-time if you try to use methods of another type.
Note that you give up compile type safety by doing it. Depending on your needs it may be ok, but consider is some alternative solution that preserve compile type safety would work better. I.e. returning single element array as shown in istme86's asnwer.
While IEnumerable<string> is the standard solution, you could also try the new StringValues struct which implicitly casts between string values and string arrays:
StringValues a = "A";
StringValues b = new string[] { "A", "B" };
StringValues c = null;
Console.WriteLine(a.Contains("A"));
// true
Console.WriteLine(b.Any(x => x == a));
// true
Console.WriteLine(c.Any());
// false

Null reference error when accessing an array

I have the following class:
public class MyClass
{
public MyArrObj[] arrOfObj; //Array of objects
}
When I try to access it using:
MyClass myClass = new MyClass;
myClass.arrOfObj = GetData();
it gives me a null exception. If it wasn't an array, I could have new'd it, but i am not sure how to handle this.
When creating an array of a reference type (MyArrObj in your case), it is required that you allocate memory for each array element (using new operator) in addition to allocating memory for the array itself.
Array of Reference types
For example, if you create an MyArrObj array,
MyArrObj[] objectArray = new MyArrObj[3];
Then the array elements objectArray[0] to objectArray[2] would still be null. A separate initialization is required.
objectArray[0] = new MyArrObj();
Only when the above step is done can you access the members of the array element.
objectArray[0].SomeMethod();
SomePropertyType readProperty = objectArray[0].SomeProperty;
If you skip the array element initialization then trying to access a member of the array element would give a System.NullReferenceException
objectArray[0].SomeMethod(); // throws NullReferenceException
because
objectArray[0]; is null.
You can check this using
if(objectArray[0] == null)
{
Console.WriteLine("objectArray[0] is null");
}
Array of Value types
If you know the array before hand, then you can use the initialization pointed out by #clonked.
For value types like built in types(like int, float, structs, etc. you need not initialize each array element.
For example, if you create an int array,
int[] intArray = new int[3];
Then the array elements intArray[0] to intArray[2] have the memoery allocated and values can be assigned to them.
intArray [0] = 1;
Check your GetData()
So your GetData() method should include code like this,
private MyArrObj[] GetData()
{
int numberOfObjects = GetNumberOfObjects(); // Get the number of objects at runtime
MyArrObj[] objectArray = new MyArrObj[numberOfObjects];
for (int i = 0; i < numberOfObjects; i++)
{
objectArray[i] = new MyArrObj();
}
return objectArray;
}
More information
Types (C# Programming Guide)
Value Types (C# Reference)
The way your are using your GetData() method, it has to return an array.
Here's a couple of ways to create an array on the fly (you'll still need to populate them, though):
int[] x = new int[5] ; // create a 5-element array of integers
int[] y = (int[]) Array.CreateInstance(typeof(int),5) ; // create a 5 element array of integers
Cheers.
You need to do something like this:
class Program
{
static void Main(string[] args)
{
MyClass myClass = new MyClass();
myClass.arrOfObj = GetData();
foreach (var item in myClass.arrOfObj)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
private static int[] GetData()
{
return new int[]{1,2,3,4,5};
}
}
public class MyClass
{
public int[] arrOfObj; //Array of objects
}
the GetData method is the important part.
it has to initialize a new array and return it.
If the signature of your GetData() method looks like public MyArrObj[] GetData() then your code should work correctly. I tested your code with 2 versions of the 'GetData' method:
public MyArrObj[] GetData()
{
return new MyArrObj[3] { new MyArrObj(), new MyArrObj(), new MyArrObj() };
}
public MyArrObj[] GetData2()
{
return new MyArrObj[0];
}
Neither of these methods threw a null reference exception. I would suspect there is something else inside of your GetData() method that is throwing the null reference, rather than the operation of assigning the results of that method to the arrOfObj field.

Categories