I regularly have to export various List<> from within my code to external files. In order to do it in a generic way I wanted to write a function that just takes the name of my variable and then exports according to the fields in that class.
I have that working fine, the only thing what I can't seem to figure out is the name of the variable that I want to export. I can get the type of the class, but that's not what I need. I can get the name of the variable I put it into but that's not what I need either.
Below you'll find the code I'm using:
public class testClass
{
public string firstName {get; set;}
public string lastName {get; set;}
}
private void test()
{
testClass nameOfMyTestClass = new testClass();
// DATA ADDED TO nameOfMyTestClass
...
exportClass(nameOfMyTestClass);
}
public void exportClass(object selectedClass)
{
string objectName = selectedClass.name;
string output = string.Empty;
// GET THE DATA FROM THE SETTINGS
PropertyInfo[] properties = selectedClass.GetType().GetProperties();
foreach (var p in properties)
{
output += "[" + p.Name + "] " + p.GetValue(selectedClass) + Environment.NewLine;
}
// WRITE THE FILE
File.WriteAllText(basePath + #"DATA\" + objectName + ".txt", output, Encoding.UTF8);
}
So I'm passing the variable nameOfMyTestClass as an object into the method exportClass because I want to be able to use this for multiple different classes.
Inside that method I would like to use the name of the variable that I have added as the name of my file: e.g. nameOfMyTestClass.txt
I can get the Class itself by using the GetType() or I can use ToString() but that gives me NAMESPACE.Form1+testClass as a result but I can't find a way to get the actual name of the variable.
Does anybody have an idea how I can accomplish this?
Thanks
You can't get variable names through reflection since they are not present in the compiled assembly's metadata.
But to get the name of the variable, you can use the nameof() operator (introduced with C# 6.0). Note that you would have to add an extra parameter for the name:
exportClass(nameOfMyTestClass, nameof(nameOfMyTestClass));
public void exportClass(object selectedClass, string name)
{ /* ... */ }
The advantage of the nameof operator is, that if you use refactoring (renaming), the passed name will be changed as well, compared to just passing the name in a string literal.
This is not possible because variables don't haves names after they are compiled to IL. But there is a Workaround for you:
void ExportClass(object obj, string className)
{
...
}
var testClass = new TestClass();
ExportClass(testClass, nameof(testClass));
You pass the variable name with nameof to the method and you can use it there.
Related
As a newbie in C#, I am trying to figure out why I have to put CardNumber as such as static in order to fit into the formatted string....
If I didn't add static string CardNumber but use string CardNumber;, the code would report an error with the last CardNumber underlined. The error goes: A field initializer cannot reference the non-static field, method, or property 'WriteXML.CardNumber'.
I know there are tons of static and non-static comments and questions out there. None of them seems to directly explain, "if using non-static in a formatted string, then _ will happen, or then _ won't make any sense". If duplicate, please kindly point out the resource at least. I really appreciate it!
class WriteXML
{
static string CardNumber;
static string ExpMo;
static string ExpYr;
static string FirstName;
static string LastName;
string xmlContent =
string.Format("<CardNumber>{0}</CardNumber>" +
"<ExpMo>{1}</ExpMo>" +
"<ExpYr>{2}</ExpYr>" +
"<FirstName>{3}</FirstName>" +
"<LastName>{4}</LastName>",
CardNumber, ExpMo, ExpYr, FirstName, LastName);
}
Field initializers run before the object is fully constructed, so they can't access other fields in the same object because those fields might not be initialized yet. The order that field initializers run in is not guaranteed.
For example, if you had something like:
public class Foo
{
string someField = "foo";
string someOtherField = someField + "bar";
}
Then the initialization of someOtherField can't happen until someField has been initialized.
So you have to initialize someOtherField somewhere else, once the object has been constructed and all the field initializers have run. One place would be the constructor:
public class Foo
{
string someField = "foo";
string someOtherField; // can't initialize yet
public Foo()
{
someOtherField = someField + "bar";
}
}
Another alternative, especially if someOtherField isn't supposed to be writable, would be to use a property:
public class Foo
{
string someField = "foo";
)string SomeProperty
{
get { return someField + "bar"; }
}
}
This defers working out what SomeProperty is until you actually try and access it and, as a bonus, if someField is changed after construction, then SomeProperty will automatically synch up to the new value.
For example:
var f = new Foo();
Console.WriteLine(f.SomeProperty); // displays "foobar"
// assuming we'd marked it public
f.someField = "la"; // assuming we'd made that public too!
Console.WriteLine(f.SomeProperty); // displays "labar"
The answer to this question is found in C# Language Specification:
A variable initializer for an instance field cannot reference the
instance being created. Thus, it is a compile-time error to reference
this in a variable initializer, as it is a compile-time error for a
variable initializer to reference any instance member through a
simple-name.
In your code below, xmlContent is an instance field that has a variable initializer than references instance members CardNumber, ExpMo, ExpYr, FirstName, LastName. They are instance members when you omit the static field modifier. So it is not up to the spec, and hence the compile-time error.
string xmlContent =
string.Format("<CardNumber>{0}</CardNumber>" +
"<ExpMo>{1}</ExpMo>" +
"<ExpYr>{2}</ExpYr>" +
"<FirstName>{3}</FirstName>" +
"<LastName>{4}</LastName>",
CardNumber, ExpMo, ExpYr, FirstName, LastName);
See #Matt Burland's answer how to work around this issue.
I am not really sure what you are trying to achieve, but maybe this code will help you.
public class Foo
{
string CardNumber { get; set;}
string ExpMo { get; set; }
string ExpYr { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
public String WriteXml()
{
string xmlContent =
string.Format("<CardNumber>{0}</CardNumber>" +
"<ExpMo>{1}</ExpMo>" +
"<ExpYr>{2}</ExpYr>" +
"<FirstName>{3}</FirstName>" +
"<LastName>{4}</LastName>",
CardNumber, ExpMo, ExpYr, FirstName, LastName);
return xmlContent;
}
}
I'll try to quote and complete your desired explanation sentence:
If using a non-static reference in a formatted string (more general, in another non-static field), then that string/field will not be able to access it because it needs in the first palce a reference to an initialized object (non-static means that it can have different values based on different objects where the field lives in). Using a static field it can access anytime without needing an initialized object.
I have a class :
class Sample
{
...
}
and define a property like this:
Sample sampleParam =new Sample(...);
and have a function :
private void Func(Sample s)
{}
and use it like:
Func(sampleParam);
can I get the 's' name in the function? I mean can I get "sampleParam"(the name of param)?
It sounds odd; but I need the name of the passed param.
and sorry for this type of asking; I just wanted to ask my question as simple as possible
public string GetParamName(System.Reflection.MethodInfo method,int index)
{
string strParameterName = string.Empty;
if (method != null && method.GetParameters().Length > index)
strParameterName = method.GetParameters()[index].Name;
return retVal;
}
Yes there is a way to achieve this through Reflection...
You should never reference variable or property names from called methods - it's bad manners and bad design (mostly the latter).
There is nameof operator in C# 6.0, but it wasn't designed for this.
You could use expression trees, which would slightly change your syntax. If sampleParam is not a property but a variable, you can't really access it, because compiler does not store any references to that name in generated dll file.
This isn't exactly what you're asking for, but is perhaps closer to what you want, but you could take a look at System.Environment.StackTrace.
I think it is not possible to get the name for a variable which value is passed to a method. But there is the compiler service CallerMemberNameAttribute which copies the name of the caller method (here the get accessor of our property Name) to the calling method if not specified:
class Person {
static void Main(string[] args) {
Person bart = new Person();
bart.Name = "Bart";
Console.ReadKey();
}
private string _name;
public string Name {
get {
return _name;
} set {
_name = value;
PropertyChanged(); //no need to fill in `Name` here! :)
}
}
//automatically copy caller's name to `propertyName`, at compile time
private void PropertyChanged([CallerMemberName] string propertyName = "") {
object propertyValue = this.GetType().GetProperty(propertyName).GetValue(this);
Console.WriteLine("Property '" + propertyName + "' changed the value to '" + propertyValue + "'");
}
}
Prints:
Property 'Name' changed the value to 'Bart'
If you mean can you get the name 'sampleParam' from INSIDE func? The the answer is no. There is nameof() in C#6.0 but 'sampleParam' inside not in scope inside the func. The variable s (of type Sample) is crated and assigned a ref to sampleParam.
You can get the name "s" inside Func.
You can get the name "sampleParam" in the calling class (outside Func).
Example (available on dotnetfiddle)
using System;
public class Program
{
public static Sample sampleParam {get; set;} =new Sample();
public static void Main()
{
Console.WriteLine($"Name of property: {nameof(sampleParam)}");
Func(sampleParam);
}
private static void Func(Sample s)
{
Console.Write($"Name of parameter: {nameof(s)}");
}
}
public class Sample
{
}
Output:
Name of property: sampleParam
Name of parameter: s
Now this is a rather simplistic example. Func exists in the same class as sampleParam and there is only one property so one could derive the name but my assumption is despite your question stating it this way you are looking for a more generalized solution. The problem is that inside func the calling parameter name is not in scope. You could capture it via nameof in the calling method and pass it into func but you shouldn't that would be horrible code for a variety of reasons.
As described what you are doing is intentionally building fragile tightly coupled code which is something developers work very hard to prevent. The caller is not going to know the name of the parameter passed into func is important and shouldn't. This leads me to believe this an xy problem.
I have a custom attribute and I would like it to have the name of a property as input. Because the name is a string it is a valid input type (as attributes are quite limited as to what they can have as input to their constructors).
But how can I accomplish this?
Take this example code:
public class MyCustomAttribute : Attribute {
public MyCustomAttribute(string propertyName) {}
}
public class Foo {
public bool MyCustomProperty { get; set; }
[MyCustom(SomeMagicAppliedToMyCustomProperty)] // I want the attribute to receive something along the lines of "Foo.MyCustomProperty"
public void Bar();
}
How can I accomplish this with the limitations to what an attribute can receive in its constructor?
There's a new feature in c#-6.0 nameof() that gives the name of the particular property, variable, class etc as a string,
http://www.c-sharpcorner.com/UploadFile/7ca517/the-new-feature-of-C-Sharp-6-0-nameof-operator/
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
This is not possible.
The attributes can accept only constants, just put your MyCustomProperty name in quotes into the Attribute.
You can also use CallerMemberNameAttribute
https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx
public CustomAttribute([CallerMemberName] string propertyName = null)
{
// ...
}
I've used this in an MVC5 application with C#6
private const string jobScheduleBindingAllowed = nameof(JobSchedule.JobId) + ", " + nameof(JobSchedule.Time) + ", " + nameof(JobSchedule.EndTime) + ", " + nameof(JobSchedule.TimeZoneId);
Then when I want to specify the bindings that are valid for the model:
[HttpPost]
public ActionResult CreateJobSchedule([Bind(Include = jobScheduleBindingAllowed)]JobSchedule jobSchedule)
{
// stuff
}
Slightly cumbersome but better than having magic strings everywhere and is type safe so that errors due to renames can be caught at compile time.
Maybe, this question is stupid, but in my specific situation i want to get instance name, so what i mean :
class Student
{
private string name {get; private set;}
public Student(string name)
{
this.name = name
}
public getInstanceName()
{
//some function
}
}
so when i make an student
Student myStudent = new Student("John");
it's stupid but i want this
myStudent.getInstanceName(); // it should return 'myStudent'
This is now possible in C# 6.0:
Student myStudent = new Student("John");
var name = nameof(myStudent); // Returns "myStudent"
This is useful for Code Contracts and error logging as it means that if you use "myStudent" in your error message and later decide to rename "myStudent", you will be forced by the compiler to change the name in the message as well rather than possibly forgetting it.
This is not possible in C#. At runtime, the variable names will not even exist, as the JIT removes the symbol information.
In addition, the variable is a reference to the class instance - multiple variables can reference the same instance, and an instance can be referenced by variables of differing names throughout its lifetime.
This question is very old, but the answer changed with the release of .Net Framework 4.6. There is now a nameof(..) operator which can be used to get the string value of the name of variables at compile time.
So for the original question C# nameof(myStudent) // returns "myStudent"
Give this a try
string result = Check(() => myStudent);
static string Check<T>(Expression<Func<T>> expr)
{
var body = ((MemberExpression)expr.Body);
return body.Member.Name;
}
Or
GetName(new { myStudent });
static string GetName<T>(T item) where T : class
{
return typeof(T).GetProperties()[0].Name;
}
Variable names exist only for your benefit while coding. Once the the code is compiled, the name myStudent no longer exists. You can track instance names in a Dictionary, like this:
var students = new Dictionary<string, Student>();
var students["myStudent"] = new Student("John");
// Find key of first student named John
var key = students.First(x => x.Value.Name == "John").Key; // "myStudent"
No, but you could do this
var myStudent = new Student("John").Named("myStudent");
var nameOfInstance = myStudent.Name();
public static class ObjectExtensions
{
private static Dictionary<object,string> namedInstances = new Dictionary<object, string>();
public static T Named<T>(this T obj, string named)
{
if (namedInstances.ContainsKey(obj)) namedInstances[obj] = named;
else namedInstances.Add(obj, named);
return obj;
}
public static string Name<T>(this T obj)
{
if (namedInstances.ContainsKey(obj)) return namedInstances[obj];
return obj.GetType().Name;
}
}
So I've been searching around for about a week trying to figure out how to do this. While gathering bits and pieces of stuff I didn't know I found a relatively simple solution.
I think the original poster was looking for something like this, because if you have to use the name of the class to find out the name of the class then what's the point..
For those saying "It's not possible" and "Why would you want to.." my particular reason is for a class library where I have a class that the app developer can call the instances whatever they want, and it's meant to have multiple instances with different names. So I needed a way to be able to identify those instances so I can use the right one for the circumstance.
public static List<Packet> Packets = new List<Packet>();
public class Packet
{
public Packet(string Name)
{
Packets.Add(this);
name = Name;
}
internal string _name;
public string name
{
get { return _name; }
set { _name = value; }
}
}
It does require that they pass in the name of the instance as I've not yet figured out how to acquire the name they're using from inside the constructor. That is likely the thing that isn't possible.
public Packet MyPacket = new Packet("MyPacket");
This creates the instance, stores a reference to it in Packets and saves it's name in the newly created instance.
To get the name associated with the Packet and connect it to a variable..
Packet NewName = Packets[Packets.FindIndex(x => x.name == "MyPacket");
Whether you use the same variable name or a new one doesn't really matter, it's just linking the instance you want to it.
Console.WriteLine(NewName.name); // Prints MyPacket
For instances with the same name you would have to come up with some other way to tell them apart, which would require another list and some logic to determine which one you want.
No, this is not possible, because it's totally ridiculous. An object can never, in any way, know the name of the variable you happen to assign it to. Imagine:
Student myStudent = new Student("John");
Student anotherStudent = myStudent;
Console.Write(anotherStudent.getInstanceName());
Should it say myStudent or anotherStudent? Obviously, it has no clue. Or funnier situations:
School mySchool = new School("Harvard");
mySchool.enroll(new Student("John"));
Console.Write(mySchool.students[0].getInstanceName());
I really would like to know what this would print out.
public void MethodSample1(Itest variable)
{
variable.TestString = "some sample data";
Itest var1 = variable;
Console.WriteLine(variable.TestString);
MethodSample2(variable);
Console.WriteLine(variable.TestString);
Console.WriteLine(var1.TestString);
}
public void MethodSample2(Itest variable)
{
variable.TestString = "testdata";
}
public interface Itest
{
string TestString { get; set; }
}
Expected both the console output lines print "some sample data" but it seems that TestString is being overwritten with the new value? is it not like "by default in C# all the values are passed by value?".
In short, how to preserve the value of "TestString" in MethodSample1?
(I ran into this problem because all my projects are based upon a single interface)
Even after preserving the value, it does reflect! strange!
For your current problem, I don't think you can prevent any method from modifying the parameter passed to it.
Your variable is a reference type object, In C# reference type's address is passed by value to a method, that is why you are seeing the change. For example if your method is written like:
public void MethodSample2(Itest variable)
{
variable = null;
}
You won't see the change in your MethodSample1 method, since the reference address is passed by value.
is it not like "by default in C# all the values are passed by
value?".
Value types are passed by value, in your case variable is a reference type.
You should see Parameter Passing C# by Jon Skeet
Not sure why you have to modify the passed object, For workaround you can create a temporary copy of the property and then set that value before existing from the method.
The parameter is indeed passed by value, but the value you are passing is a reference to the original object.
If you want to preserve the original object's properties, you'll need to pass a copy of the original object. You could add a Clone() method to the interface or something similar:
public interface ITest
{
string TestString { get; set; }
ITest Clone();
}
public class Test : ITest
{
string TestString { get; set; }
ITest Clone() {
return new Test() {
TestString = this.TestString
};
}
}
Or, you could rethink your current approach. Do you really need to change the property of the interface? Or could you use a variable of type string instead?
how to preserve the value of "TestString" in MethodSample1?
Store it in a local variable.
public void MethodSample1(Itest variable)
{
variable.TestString = "some sample data";
string localTestString = variable.TestString;
Console.WriteLine(variable.TestString);
MethodSample2(variable);
variable.TestString = localTestString;
Console.WriteLine(variable.TestString);
}
But, this is wrong way of doing things. If you tell a little more what do you want to achieve, we could help more.