Hey I have the following two classes:
public class YoloCocoP7Model : YoloModel
{
public override int Width { get; set; } = 640;
public override int Height { get; set; } = 640;
public YoloCocoP7Model()
{
}
}
public class YoloScorer<T> : IDisposable where T : YoloModel
{
public YoloScorer(string weights, SessionOptions opts = null) : this()
{
_inferenceSession = new InferenceSession(File.ReadAllBytes(weights), opts ?? new SessionOptions());
}
}
Now I can call a function like this:
public YoloScorer<YoloCocoP7Model> _scorer;
I want to change the Width and Height inside the YoloCocoP7Model class and Initialize it to the YoloScorer class and tried it the following way:
var test = new YoloCocoP7Model();
test.Width = 10;
test.Height = 10;
This works but however If I want to use that changed class then with:
var _scorer = new YoloScorer<test>;
I get an error saying "test" is a "variable" but is used like "Type".
How can I use the changed Class then?
The YoloScorer<T> in your class definition is a generic class, meaning it can work for different types.
You can now implement methods with that type like public T GetNewObject() or public string[] GetAllPropertyNames<T>() that use that Type T.
The type is, however, not the object itself, it's the type of object. In your case, the type is YoloCocoP7Model. The type has no instance.
If you want to give your class YoloScorer a object YoloCocoP7Model, you need to declare a member of that type and add it, i.e. via constructor:
public class YoloScorer : IDisposable
{
public YoloModel Model {get; set;};
public YoloScorer(YoloModel model) : this()
{
Model = model;
}
}
Then, you can modify it by calling
var _scorer = new YoloScorer<test>;
_scorer.Model.Width = 1337;
Adding <T> to a class makes it generic. That means it can work with values of different types. For instance, the List class you might have used before is generic. It can store elements of any types you provide it with.
Therefore, when you want to create a list of type int, for instance, you type new List<int>().
Now, the class you defined is also generic. That means, when you create an instance of it with new YoloScorer<...>(), you create a new YoloScorer that works with objects of type .... This is not exactly what you want, from my understanding.
Instead, you want to pass the specific YoloCocoP7Model to this class. To do that, just add a parameter corresponding to it to the constructor:
public YoloScorer(string weights, YoloCocoP7Model model, SessionOptions opts = null)
Now you can access model from the inside (and probably store it in some sort of private variable to make it accessible at later stages). From my understanding, your class does not need to be generic (<T> is not necessary).
I have a class (students) with say 2 strings and an int array
A second class then does this:
List<student> myS = new List<student>();
All of this works correctly and I have a list containing multiple students. I am however having difficulty accessing the values within the int[].
So, my student list is populated from a database and into a list.
I then have a generic class which has a parameter List which I then want as a data table. The generic class will be called for the student class, subjects class and other classes - some of which may contain arrays and some which do not.
If I debug and step through, I get the following:
(() myS.stu[6].termMark[1] and the value is 50. However if I enter int d =stu[6].termMark[1] the error is T does not contain a definition for termMark and no extension method 'termMark' accepting a first argument of type T could be found.
int d = myS[0][1] returns the error
cannot apply indexing with [] to an expression of type 'T'
. I have tried various things like creating a separate list and adding it to myS. Nothing works.
Thanks in advance for the help.
I am fairly new to this and probably missing the obvious...
You must read about Generic Constraints
You cannot do much with an argument of type T.
If your function accepts arguments of type student , add a constraint like this
private static void NewMethod<T>(List<T> myS) where T : student
{
int d = myS[0].IntArray[1];
}
The following is not related your problem...
If you add an indexer to your class you can make it a little shorter
public class student
{
public int[] IntArray;
public int this[int x]
{
get
{
return IntArray[x];
}
}
}
private static void NewMethod<T>(List<T> myS) where T : student
{
int d = myS[0][3];
}
Perhaps the type T does not hold any definition for indexers. But, your field (int[]) does. So, instead of calling the indexer upon your object (student) call it on the field of it. So, for this answer I would assume that your object is declared as class like this
public class student {
public string S { get; set; }
public string s { get; set; }
public int[] integers { get; set; }
}
Now, you can indeed call the indexers on the integers field but not on the student object itself (it does not have any indexing mechanism). Such as,
int d = myS[0].integers[1]; // <-- your code should be
The above code (if compiled) would give you the element at the index 1 (2nd element) of the first object in the list. You were instead calling the element at index 1 of the student (which does not have indexers?).
How and "could be" organized return from the method which returns tuple type with the name of parameters,
as an example
private static Tuple<string, string> methodTuple()
{
return new {Name = "Nick", Age = "Twenty"}; /*exception because need to new Tuple<string, string>(){Item1 = "Nick", Item2 = "Twenty"}o*/
}
and call parameters like methodTuple.Name not like methodTuple.Item1....N
Is this possible or not?
UPD: I want to create object with named parameters without new named type.
In C# 7.0 (Visual Studio 2017) there is a new option to do that:
(string first, string middle, string last) LookupName(long id)
Starting C# v7.0, it is now possible to give custom name to tuple properties. Earlier they used to have default names like Item1, Item2 and so on. Let's look at few variations which is now possible:
Naming the properties of Tuple Literals:
var personDetails = (Name: "Foo", Age: 22, FavoriteFood: "Bar");
Console.WriteLine($"Name - {personDetails.Name}, Age - {personDetails.Age}, Favorite Food - {personDetails.FavoriteFood}");
The output on console:
Name - Foo, Age - 22, Favorite Food - Bar
Returning Tuple (having named properties) from a method:
static void Main(string[] args)
{
var empInfo = GetEmpInfo();
Console.WriteLine($"Employee Details: {empInfo.firstName}, {empInfo.lastName}, {empInfo.computerName}, {empInfo.Salary}");
}
static (string firstName, string lastName, string computerName, int Salary) GetEmpInfo()
{
//This is hardcoded just for the demonstration. Ideally this data might be coming from some DB or web service call
return ("Foo", "Bar", "Foo-PC", 1000);
}
The output on console:
Employee Details: Foo, Bar, Foo-PC, 1000
Creating a list of Tuples having named properties:
var tupleList = new List<(int Index, string Name)>
{
(1, "cow"),
(5, "chickens"),
(1, "airplane")
};
foreach (var tuple in tupleList)
Console.WriteLine($"{tuple.Index} - {tuple.Name}");
Output on console:
1 - cow
5 - chickens
1 - airplane
Note: Code snippets in this post are using string interpolation feature of C# which was introduced in version 6 as detailed here.
You need to declare a helper class to do so.
public class MyResult
{
public string Name { get; set; }
public string Age { get; set; }
}
What you're trying to return is an anonymous type. As the name suggests you don't know what its name is, so you can't declare your method to return it.
Anonymous Types (C# Programming Guide)
You cannot declare a field, a property, an event, or the return type
of a method as having an anonymous type. Similarly, you cannot declare
a formal parameter of a method, property, constructor, or indexer as
having an anonymous type. To pass an anonymous type, or a collection
that contains anonymous types, as an argument to a method, you can
declare the parameter as type object. However, doing this defeats the
purpose of strong typing. If you must store query results or pass them
outside the method boundary, consider using an ordinary named struct
or class instead of an anonymous type.
Update
C#7 introduces Tuple support built into the language and it comes with named tuples
(string name, int age) methodTuple()
{
(...)
}
Read more on learn.microsoft.com: https://learn.microsoft.com/en-us/dotnet/articles/csharp/csharp-7#tuples
This is not possible with Tuple, no. You'll need to create your own new named type to do this.
Now you can do it with tuple Name in C#
For Lambda Expression:
private static (string Name, string Age) methodTuple() => ( "Nick", "Twenty" );
Or
private static (string Name, string Age) methodTuple()
{
return ( "Nick", "Twenty" );
}
Do not use class type for Tuple. Use primitive type to set the name in Tuple.
I usually create a new type that derives from Tuple, and map your explicit properties to return the base class's ItemX properties.
eg:
public class Person : Tuple<string, string>
{
public Key(string name, string age) : base(name, age) { }
public string Name => Item1;
public string Age => Item2;
}
Unfortunately, this is not possible using the "Tuple" type, as it is defined as "Item1...N" in MSDN. So this exception is valid.
This method can compile in 3 ways:
1.) Change return type to object - this will create an "anonymous" type, which you can then use later. It is not particularly useful if you want to access the "Name" or "Age" property later without some additional work.
2.) Change return type to dynamic - this will let you access the "Name" and "Age" property, but will make the entire program (just the DLL where this method is located really) slightly slower as the use of dynamic necessitates throwing out some strong typing.
3.) Create a class and use it as teh return type.
Sample code here:
private static object ObjectTuple()
{
return new { Name = "Nick", Age = "Twenty" };
}
private static dynamic DynamicTuple()
{
return new { Name = "Nick", Age = "Twenty" };
}
private static Temp TempTuple()
{
return new Temp{ Name = "Nick", Age = "Twenty" };
}
class Temp
{
public string Name { get; set; }
public string Age { get; set; }
}
As per me, when you want to return or get many things from a single method, better make its return type as CLASS but if you intend to use Tuple which itself is Class then for better naming this new class should inherit from Tuple. e.g. mentioned below.
public CustomReturn ExecuteTask( int a, string b, bool c, object d )
{
// Calling constructor of CustomReturn Class to set and get values
return new CustomReturn(a,b,c,d);
}
internal class CustomReturn
// for tuple inherit from Tuple<int,string,bool,object,double>
{
//for tuple public int A{ get {this.Item1} private set;}
public int A{get;private set;}
public string B{get;private set;}
public bool C{get;private set;}
public object D{get;private set;}
public CustomReturn (int a, string b, bool c, object d )
// use this line for tuple ": base( obj, boolean )"
{
this.A = a;
this.B = b;
this.C = c;
this.D = d;
}
}
Main(args)
{
var result = ExecuteTask( 10, "s", true, "object" );
// now if u have inherited Tuple for CustomReturn class then
// on doing result. you will get your custom name as A,B,C,D for //Item1,Item2,Item3,Item4 respectively also these Item1,Item2,Item3,Item4 will also be there.
}
I have a search page which is using strongly typed objects, but I have the values broken into specific groups.
Code behind page calls the following when the user clicks the search button (none of these fields are empty):
SearchCriteria sc = new SearchCriteria();
sc.Generic.id = txtId.Text;
sc.Generic.maxReturned = rblMaxReturned.SelectedIndex;
sc.DisplayOnly.category = txtCategory.Text;
sc.DisplayOnly.type = txtType.Text;
sc.Building.address = txtAddress.Text;
sc.Building.city = txtCity.Text;
The DataType file is defined like this:
[Serializable]
public class SearchCriteria
{
public _Generic Generic { get;set; }
[Serializable]
public class _Generic
{
public int id {get;set;}
public int maxReturned {get;set;}
}
public _DisplayOnly DisplayOnly { get;set; }
[Serializable]
public class _DisplayOnly
{
public int category {get;set;}
public int type {get;set;}
}
public _Building Building { get;set; }
[Serializable]
public class _Building
{
public int address {get;set;}
public int city {get;set;}
}
}
When the code executes, I get a nullreferenceerror even though all the items in the various textboxes have a value. However, if I take out the public _Building Building { get;set; } and call the class directly it works and populates the values. What's the best solution here? Should I not use intermediary definition and call the class directly? If so, how can I call the different groups without making four different calls on the code behind page?
You need to initialize the internal class instances. Simply declaring the variables doesn't mean that you can access their properties without creating the instances. You could easily do that in the constructor of the SearchCriteria class
[Serializable]
public class SearchCriteria
{
public SearchCriteria()
{
// Without these initialization the internal variables are all null
// and so assigning any property of a null object causes the error
Generic = new _Generic();
DisplayOnly = new _DisplayOnly()
Building = new _Building();
}
.....
}
When you create a new instance of your SearchCriteria class, the properties are not initialized, and so they all have a value of null. So now look at the very first line where you try to use one of those properties:
sc.Generic.id = txtId.Text;
Here, txtID.Text is perfectly fine, but sc.Generic is null. When you try to look up the it's .id property for assignment, that's where the exception is thrown.
To fix this, you need to initialize each of those properties to have an instance of their type. Additionally, it's probably a good idea to use a private set, like so:
public _Generic Generic { get;private set; }
This will still allow to make all the same assignments that are currently written, because that only requires a get action to retrieve the type instance. The assignment/set operation is on the property of the property.
I have few nested classes like "BlueprintsManager.WorkingStandardBlueprint", and ArrayList that have instances of them. I would like to pass them to a method as a parameter, eg:
private void ShowBlueprints(string class_str, ArrayList class_array)
{
// class_str would be passed as "BlueprintsManager.WorkingStandardBlueprint"
// how here I can access class_str as a class and cast class_array to it, to access some variables.
// for example, I need to access some BlueprintsManager.WorkingStandardBlueprint public variables.
}
I was messing around with Reflections, Generics but I still can't make it work.
Thank you.
You should use generics for this:
private void ShowBlueprints<T>(List<T> class_array)
{
for (BlueprintsManager.WorkingStandardBlueprint item in class_array)
{
if(typeof T is BlueprintsManager.WorkingStandardBlueprint)
{
Console.WriteLine(((BlueprintsManager.WorkingStandardBlueprint)item).whateverpropertyyouhavedefined);
}
}
}
Now you can call the method like this:
ShowBluePrints<BlueprintsManager.WorkingStandardBlueprint>(myblueprints);
EDIT In the comments the OP says that all properties are the same. This solution would work:
class BaseClass
{
string Name {get; set;}
int id {get; set;}
}
class BlueprintsManager
{
class WorkingStandardBlueprint : BaseClass
{
}
}
private void ShowBlueprints<T>(List<T> class_array) where T : BaseClass
{
for (T item in class_array)
{
Console.WriteLine(item.Name);
}
}
I think the question is why more than how. I didn't see many constructs like that.
You should use generics if that satisfies what you need.
And if you really need to construct types dynamically from an arbitrary list of itmes
1) Make a generic method (like suggested already)
interface IBlueprint
{
int ID {get;set;}
int Name {get;set;}
}
class MyClass
{
private void ShowBlueprints<T>(IEnumerableT> values) where T : IBlueprint
{
// access properties of IBlueprint
}
// I presume you 'know something' about your 'T'-s, have an interface -
// ...if you don't you should if possible
}
2) And call it like this (I typed in from memory but it should be correct)
MyClass myclass = new MyClass();
var values = // list of your blueprints
// if you don't have any in the list handle it and bail out
MethodInfo methodInfo = typeof(MyClass).GetMethod("ShowBlueprints");
MethodInfo methodInfoGeneric =
methodInfo.MakeGenericMethod(new[] { values.First().GetType() });
// or get your blueprint type from string if needed
methodInfoGeneric.Invoke(myclass, new object[] { values });
You cannot cast an object to a type that you only know by its string name. Therefore you also cannot access its fields that way. You have some options to access the fields of a type:
You know the exact type (or any of its base types or interfaces), so you can cast directly:
object first = class_array[0];
var blueprint = (BlueprintsManager.WorkingStandardBlueprint)first;
blueprint.MyProperty = 10;
You don't know the exact type but you are very sure it has a public property/field with a particular name. Note the dynamic keyword here, works in C# 4 and higher.
dynamic blueprint = class_array[0];
blueprint.MyProperty = 10;
You don't know the exact type, but you get a string with the type's name. And you don't know the exact property/field, but you get a string with the property's name. Then you can use reflection:
string typeName = "BlueprintsManager.WorkingStandardBlueprint";
string propertyName = "MyProperty";
var type = Assembly.GetExecutingAssembly().GetType(typeName);
var property = type.GetProperty(propertyName);
object first = class_array[0];
// Getter:
int result = (int)property.GetMethod.Invoke(first, null);
// Setter
property.SetMethod.Invoke(first, new object[] { 10 });
By the way, you shouldn't be using an ArrayList. It is a very old class from when generics didn't exist yet. Today you should use List<T>. For example, when you know all T implement an interface IBlueprint with the properties you want to use:
private void ShowBlueprints<T>(string classStr, List<T> classArray)
where T : IBlueprint
{
T blueprint = classArray[0];
blueprint.MyProperty = 10;
}
Or if you really have a list of objects of any type:
private void ShowBlueprints(string classStr, List<object> classArray);