This question already has answers here:
c# sort a list by a column alphabetically
(2 answers)
Closed 6 years ago.
I am trying to write my first c# program and I can't keep getting the error below. Can someone explain why I am getting this error? Everything has a type when it is declared, where is the void coming from? I am writing the code on https://repl.it/languages/csharp if it is important.
using System;
using System.Linq;
using System.Collections.Generic;
class MainClass {
public static void Main (string[] args) {
List<string> mylist = new List<string>() { "2","1","2","3","3","4" };
mylist=mylist.Sort();
foreach(var item in mylist)
{
Console.Write(item.ToString());
}
}
}
Error:
Cannot implicitly convert type `void' to `System.Collections.Generic.List<string>'
List<T>.Sort returns void as it actually modifies the List it is called on (it doesn't return a new collection). Hence assigning the method's return value to a List is a compile error.
If you want a sort that does not modify the underlying collection, consider using OrderBy and ToList if you want to eager-enumerate the result.
mylist.Sort(); doesn't return List, it returns void, you shouldn't assign it to a list.
After calling Sort your entire list is already sotred.
Related
This question already has answers here:
Check if instance is of a type
(9 answers)
Closed 2 years ago.
So I'm creating a generic method, obviously the signature therefore can take any type of object. The problem I have is I want the user to be able to either just send one object to my method, or a collection (I know, different interface from IEnumerable) of objects. Now because (T obj) could be one object, or it could be a list or an array of objects, Im not sure how to check and alter behaviour depending on if it is a collection or not.
This is what I have, and I'm getting a compiler error CS1061, T does not contain a definition for FirstOrDefault. Which makes sense, because it might not. But as far as I can tell my conditional should have checked if it does or not.
if (typeof(IEnumerable<T>).IsAssignableFrom(obj.GetType()))
{
foreach (var info in obj.FirstOrDefault().GetType().GetProperties())
{
members.Add(info.Name, info.GetValue(obj).ToString());
}
}
else
{
foreach (var info in obj.GetType().GetProperties())
{
members.Add(info.Name, info.GetValue(obj).ToString());
}
}
In case it makes any difference, members is a Dictionary<string, string>. At this stage I'm only trying to get it to accept both collections and single objects, I can work out the behaviour later.
You have to cast obj to IEnumerable<T> before you can access FirstOrDefault. And in your case, you can do that with the is operator.
if (obj is IEnumerable<T> e)
{
foreach (var info in e.FirstOrDefault().GetType().GetProperties))
...
}
...
Your code was fine as is, except you were using obj without first casting. For example you could have done this instead:
if (typeof(IEnumerable<T>).IsAssignableFrom(obj.GetType()))
{
var e = (IEnumerable<T>)obj;
foreach (var info in e.FirstOrDefault().GetType().GetProperties())
{
members.Add(info.Name, info.GetValue(obj).ToString());
}
...
As you can see though, using is is shorter and easier to read.
This question already has answers here:
How do I clone a generic list in C#?
(29 answers)
C# Value and Reference types
(6 answers)
Closed 4 years ago.
class ListHolder
{
public List<char> List;
public ListHolder(List<char> l)
{
this.List = l;
}
}
class Program
{
static void Main(string[] args)
{
List<char> a = new List<char>();
a.Add('s');
ListHolder c = new ListHolder(a);
a.Clear();
Console.WriteLine(c.List.Count);
}
}
I've put some list into that class, than I cleared the list and wrote the count of the list in the class... I would expect that the output should be "1" (as the list in the class contains the letter 's') but instead it writes "0". How is possible, that a.Clear clears even the list in the class? How can I achieve clearing only the list in the Main and the list in the class letting be?
Since you are passing a reference instead of the list itself, you will get 0 after clearing your list.
What you have to do is passing to the class a new List containing the other list's values:
cl c = new cl(new List<char>(a));
This way, even if you clear the 'main' list, in your class you'll have 1 as items count.
Hope this helps.
List and other classes are reference types. In few words, it means you have an object somewhere in memory and a reference(s) on it.
this.l = l; means you copied the reference to the first list to the class field. So you have one list and two references on it. And when you clear the list via a variable, no matter how you address it after clearing - via a or cl.l. Your single list is cleared already.
If you want to avoid this, you need to create a copy of list in your constructor:
public cl(List<char> l)
{
this.l = new List<char>();
this.l.AddRange(l);
}
}
I recommend you to read more information about reference types. They are used widely and knowledge about them will give you a good base for programming skills.
if (a is System.ValueType)
{
//never
Console.WriteLine("List is value type");
}
if ('s' is System.ValueType)
{
//always
Console.WriteLine("char is value type");
}
I think you know, char is value type, but list is reference type.
Even code like this; it would be same.
List<char> a = new List<char>();
a.Add('s');
List<char> c = a;
a.Clear();
Console.WriteLine(c.Count);
I have the following code
using System;
using System.Collections.Generic;
public class Test
{
public static void Main()
{
List<object> list = new List<object>();
list.Add(new {
Value = 0
});
//Console.WriteLine(list[0].Value);
}
}
Is there a simple way to write the commented line of code without causing a compile time error? I know I could resort to using the dynamic keyword or implementing an extension method for the Object class that uses Reflection, but is there maybe another more acceptable way?
The goal is the avoid creating a class since its only purpose would be to store data. But at the same time still be able to retrieve the data later on in the program. This code will eventually end up in a web method which is why I want it to be dynamic / anonymous in the first place. All of the objects would end up having the same properties, as they would be storing values stored in tables, but the values are needed later for other calculations.
Is there a simple way to write the commented line of code without causing a compile time error?
Not with the way you've declared the list. If your list will contain only objects of that anonymous type, you could use an array initializer and convert it to a List<{anonymous type}>:
var list = (new [] {
new { Value = 0 }
}).ToList();
Console.WriteLine(list[0].Value);
The nice thing is that you can add to the list easily, since anonymous types with the same properties are merged into one type by the compiler:
list.Add(new {Value = 1});
Per Servy's comment, a method to avoid an array creation would just be:
public static List<T> CreateList<T>(params T[] items)
{
return new List<T>(items);
}
usage:
var list = CreateList(new { Value = 0 });
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
List<int> in c#
I have the following program. I am confused about the output.
The line -
Console.WriteLine(listIsARefType.Count) prints 0 instead of 1. Any idea why ?.
class Program
{
static void Main(string[] args)
{
ListTest d = new ListTest();
d.Test();
}
}
class ListTest
{
public void ModifyIt(List<int> l)
{
l = returnList();
}
public void Test()
{
List<int> listIsARefType = new List<int>();
ModifyIt(listIsARefType);
Console.WriteLine(listIsARefType.Count); // should have been 1 but is 0
Console.ReadKey(true);
}
public List<int> returnList()
{
List<int> t = new List<int>();
t.Add(1);
return t;
}
}
Everything in .Net is passed by value by default, even reference types. The difference with reference types is that it's the reference itself that's passed by value. So when you call the ModifyIt() function, you pass a copy of the reference to the function. That function then changes the copied reference. The original list reference is still intact and the list is unchanged. Your code should go like this:
void ModifyIt(List<int> t)
{
t.Add(1);
}
You'll see that now the list does change. You could also do it like this:
void ModifyIt(ref List<int> t)
{
t = returnList();
}
However, you should favor the former style vs the latter. If you already have something like a returnList() function and you really need a function to add those items to an existing list, do it like this:
void ModifyIt(List<int> t)
{
t.AddRange(returnList());
}
Your ModifyIt method name is misleading: it does not modify the list, it replaces it with a new list.
So while List<int> is a reference type you are passing the reference by value. When you change it to point to a new List inside the method that does not affect the reference in the calling method.
You're creating a new instance of List<int> and assigning it. You cannot do that, because when the function returns, the parameter you passed will still be a reference to the old list.
What you need to do is use the ref parameter like so:
public void ModifyIt(ref List<int> l)
{
l = returnList();
}
And then call it like so:
ModifyIt(ref listIsARefType);
You will find that that will function as expected.
The following line:
List<int> listIsARefType = new List<int>();
creates a new list. The number of items in the list is 0. The next line "modifies" the list (but not really, as that is the problem!)
ModifyIt(listIsARefType);
So while you think you've added something, you actually have not. Let's look at what happens when the ModifyIt( ) gets called:
l = returnList();
simply assigns a List<int> of Count 1 to l. But - here's the important part:
The reference l is only the parameter. You have not changed what listIsARefType references. And so, listIsARefType still contains 0 items. l contains 1 item but it is lost as soon as ModifyIt( ) is complete.
Other people can better explain what's happening here, and it's important to know. Jon Skeet has this excellent article on Parameter passing in C#.
This is more a question regarding generics than subsonic:
Imagine if have the following code:
List<int> result =
DB.Select(Product.Columns.Id)
.From<Product>()
.ExecuteTypedList<int>();
That works great and returns a generic list with the ids from my Product table.
But if I want to get a list of the ProductName:
List<String> result =
DB.Select(Product.Columns.ProductName)
.From<Product>()
.ExecuteTypedList<String>();
it throws a compiler message (translated from german):
"string" has to be a non-abstract type
with a public Constructor without
parameter, in order to be used as a
generic type or in the generic method
"SubSonic.SqlQuery.ExecuteTypedList()"
as param "T".
cause: String has no empty contructor:
int i = new int; // works
String s = new String; // compiler error: "string" does not contain a constructor that takes '0' argument
If I use a List<Object> instead it works, but is there a more elegant way, where I can use List<String> ?
Update: List<Object> does not work. I indeed get a list of objects but that seem to be "empty" object that doesn't contain my ProductNames ( object.ToString() returns {Object} )
With a little bit dotnet magic it is possible without patching the subsonic code.
Create a new class SubsonicSqlQueryExtensionMethods and drop this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SubSonic;
namespace MyUtil.ExtensionMethods
{
public static class SubSonicSqlQueryExtensionMethods
{
public static List<String> ExecuteTypedList(this SqlQuery qry)
{
List<String> list = new List<String>();
foreach (System.Data.DataRow row in qry.ExecuteDataSet().Tables[0].Rows)
{
list.Add((String)row[0]);
}
return list;
}
}
}
Now add a reference to MyUtil.ExtensionMethods to your class:
using MyUtil.ExtensionMethods;
And finally this works:
List<String> result = DB.Select(User.Columns.Name).From<User>().ExecuteTypedList();
Please note that the above extension method overloads the ExecuteTypedList() method with no type-argument (unfortunately this snippet requires dotnet 3.5, but for me it works)
I know I am late to this party but I found a neat way of 'tricking' this problem.
List<String> result =
DB.Select()
.From<Product>()
.ExecuteTypedList<String>().Select(p => p.ProductName).ToList<String>();
This works like a charm for me.
Hope is helps someone somewhere, As I am sure you are far past the issue.
Well, I can think of this, but it is hardly more elegant:
List<string>result=DB.Select(Products.Columns.ProductName)
.From<Product>()
.ExecutTypedList<StringBuilder>()
.ConvertAll(sb=>sb.ToString());
It doesn't look like SubSonic has proper support for ExecuteTypedList with string values. The method that builds the list first looks for a SubSonic type and if there's no match checks to see if T is a value type. Since string isn't a value type it falls through to the last condition which tries to match property names to the column names returned. Since string doesn't have a property name that you can assign, it fails.
See: BuildTypedResult method: http://subsonicproject.googlecode.com/svn/trunk/SubSonic/SqlQuery/SqlQuery.cs