I've been trying to call a method with a string, but one side-effect is that a new object is created everytime I press the button. How can I get rid of this? I've tried using null, but no luck.
First attempt:
string methodName = cboOriginal.Text + "To" + cboConverted.Text;
Type numeralType = typeof(NumeralSystemConversion);
ConstructorInfo numeralConstructor = numeralType.GetConstructor(Type.EmptyTypes);
object numeralObject = numeralConstructor.Invoke(new object[] { });
MethodInfo numeralMethod = numeralType.GetMethod(methodName);
object numeralValue = numeralMethod.Invoke(numeralObject, new object[] { txtOriginal.Text });
txtConverted.Text = numeralValue.ToString();
numeralType = null; numeralConstructor = null; numeralObject = null;
numeralMethod = null; numeralValue = null;
Second attempt:
string methodName = cboOriginal.Text + "To" + cboConverted.Text;
convert = typeof(NumeralSystemConversion).GetMethod(methodName).Invoke(typeof(NumeralSystemConversion).GetConstructor(Type.EmptyTypes).Invoke(new object[] { }), new object[] { txtOriginal.Text });
txtConverted.Text = convert.ToString();
convert = null;
The 'convert' object is created when the app starts. And NumeralSystemConversion is a class I have created, where the methods are located.
What I am seeing is that the memory usage in the Diagnostic Tools (Visual Studio 2015 Community) increase each time I press the button.
The biggest "clean-up" I see is to create the two object arrays once (at startup) and store them in member variables:
object[] param1 = new object[] { };
object[] param2 = new object[] { null };
Then in your method:
param2[0] = txtOriginal.Text;
convert = typeof(NumeralSystemConversion).GetMethod(methodName)
.Invoke(typeof(NumeralSystemConversion).GetConstructor(Type.EmptyTypes)
.Invoke(param1), param2);
param2[0] = null;
I don't think you can prevent it from creating strings. That's the cost of doing business, I'm afraid. What might help is to use an explicit StringBuilder as another class member:
StringBuilder methodNameBuilder = new StringBuilder();
And in your function:
methodNameBuilder.Clear();
methodNameBuilder.Append(cboOriginal.Text);
methodNameBuilder.Append("To");
methodNameBuilder.Append(cboConverted.Text);
string methodName = methodNameBuilder.ToString();
Finally, the memory increases you're seeing could just be garbage, in which case GC.Collect(); should clean them up. But it's probably better just to leave it alone, in that case.
Related
I am working on an application that should compile and debug C# code on the fly.
A simplified version of the code is included below.
What should be changed in this code to run the generated method step by step and get the state of the variables x and y after each step?
If everything should be changed that is okay, I am happy with any constructive response.
EDIT: to clarify: what I want to do is have my code debug the code that is generated with reflection, not the debug function in Visual Studio.
string code =
#"
namespace MyNameSpace
{
public class MyClass
{
public static int MyMethod()
{
var x = 3;
var y = 4;
return x * y;
}
}
}";
string namespaceName = "MyNameSpace";
string className = "MyClass";
string methodName = "MyMethod";
string language = "csharp";
string classFullname = namespaceName + "." + className;
CodeDomProvider provider = CodeDomProvider.CreateProvider(language);
CompilerParameters parameters = new CompilerParameters();
CompilerResults results;
parameters.OutputAssembly = "Compiler";
parameters.CompilerOptions = "/t:library";
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = false;
parameters.IncludeDebugInformation = true;
results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count != 0)
{
throw new Exception("Code compilation errors occurred.");
}
var instance = results.CompiledAssembly.CreateInstance(classFullname, false);
// TODO run the method step by step and get the state after each step
This configuration may help you:
parameters.GenerateInMemory = false; //default
parameters.TempFiles = new
TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true);
parameters.IncludeDebugInformation = true;
parameters.TempFiles.KeepFiles = true
To debug the generated code you will need the pdb files. To have those while debugging your application, just have to tell the compiler where to save the temporary files. For this you can just add the following line to your parameters:
parameters.TempFiles = new TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true);
You can then step into the Invokation of your targeted method. The Code could look like this:
var method = instance?.GetType().GetMethod(methodName);
method?.Invoke(instance, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture);
If you want the Debugger to automatically stop, when entering your "MyMethod", you can modify your string likes this:
string code =
#"using System.Diagnostics;
namespace MyNameSpace
{
public class MyClass
{
public int MyMethod()
{
Debugger.Break();
var x = 3;
var y = 4;
return x * y;
}
}
}";
Elchido pointed out in the comments that maybe I should look for an interpreter. After a bit of searching I came across CSI: A Simple C# Interpreter.
https://www.codeproject.com/Articles/10212/CSI-A-Simple-C-Interpreter
After investigating, my conclusion is that it is possible to use either and interpreter or the Codedom compiler to create debugger-like functionality, but it takes a significant effort.
The solution that I am working on involves splitting the code into separate statements and put all variables in an array.
The 'MyMethod' function is split into parts:
public static object Line1()
{
return 3;
}
public static object Line2()
{
return 4;
}
public static object Line3(object x, object y)
{
return x*y;
}
After compiling the code using Codedom compiler, I can do the following:
Dictionary<string, object> vars = new Dictionary<string, object>();
List<MethodInfo> lines = new List<MethodInfo>();
lines.Add(type.GetMethod("Line1"));
lines.Add(type.GetMethod("Line2"));
lines.Add(type.GetMethod("Line3"));
vars["x"] = lines[0].Invoke(instance, new object[] { });
vars["y"] = lines[1].Invoke(instance, new object[] { });
vars["#return"] = lines[2].Invoke(instance, new object[] { vars["x"], vars["y"] });
Note that this is not a working solution yet, a lot of work still has to be done to convert the 'MyMethod code' into separate lines and extract the variables. I will post an update when I have more/better code.
Click just left side of your code it will mark red dot that is called break point.After that when your code execute at the point it will break at the point and you can debug step by step bt pressing F10 key.
I have MyServiceLibrary.dll which represents few classes such as UserStorageService, Storage, User. The service contains a storage and saves users into storage.
I created a new console application with new application domain inside it.
AppDomain masterDomain = AppDomain.CreateDomain("servicedomain");
string serviceLibraryPath = #"G:\Git\Service";
Assembly serviceAssembly = Assembly.LoadFrom(serviceLibraryPath);
Here I get all types which I use.
Type userType = serviceAssembly.GetType("MyServiceLibrary.User");
Type storageType = serviceAssembly.GetType("MyServiceLibrary.UserStorage");
Type userStorageServiceType = serviceAssembly.GetType("MyServiceLibrary.UserStorageService");
New instances of these types were creted into masterDomain.
var storage = masterDomain.CreateInstanceFromAndUnwrap(serviceLibraryPath, storageType.FullName);
var user = masterDomain.CreateInstanceFromAndUnwrap(serviceLibraryPath, userType.FullName);
var userStorageService = masterDomain.CreateInstanceFromAndUnwrap(
serviceLibraryPath, // assemblyFile
userStorageServiceType.FullName, // typeName
false, // ignoreCase
BindingFlags.CreateInstance, // bindingAttr
default(Binder), // binder
new object[] {storage}, // args
CultureInfo.CurrentCulture, // culture
new object[] {} // activationAttributes
);
All of my types which I used were inherited from MarshalByRefObject class.
Now I want to add my new user.
MethodInfo addMethod = userStorageServiceType.GetMethod("Add");
addMethod.Invoke(userStorageService, new object[] { user });
I got an exception:
TargetException: The object does not match the target type.
In logfile I saw that the instance of UserStorageService was created. I can call a static method of this class, but instance methos don't work.
did you try using GetType to be sure you reflected the exact type?
userStorageService.GetType().GetMethod("Add")
Sorry -- this should have been submitted under 'ref' arguments. I was butting my head on on the issue and not really thinking. The answer is that the ModifiedSerialized function should take a ref argument to modify 'target' directly.
ModifySerialized( ref DataClass target ) {...}
Given this runtime.serializable class:
[DataContract]
public class DataClass
{
[DataMember(Order = 0)]
public int Number
{
get;
set;
}
[DataMember(Order = 1)]
public string Name
{
get;
set;
}
override public string ToString()
{
return "DataClass: " + Name + " -- " + Number;
}
}
and some exercising class:
class Test
{
public Test()
{
DataClass testDataClass = new DataClass() { Name = "Foo", Number = 123 };
ModifySerialized(testDataClass);
Console.WriteLine(testDataClass);
}
private void ModifySerialized(DataClass target)
{
MemoryStream stream = new MemoryStream();
DataContractSerializer serializer = new DataContractSerializer(typeof(DataClass));
serializer.WriteObject(stream, new DataClass() { Name = "serialized", Number = 777 });
stream.Seek(0, SeekOrigin.Begin);
string sDebug = ASCIIEncoding.ASCII.GetString(stream.GetBuffer());
Console.WriteLine(sDebug);
target = (DataClass)serializer.ReadObject(stream);
Console.WriteLine(target);
}
}
I'd expect the output to console from within the Test ctr, after the call to ModifySerialized(target) to be something like:
DataClass: name = serialized -- number = 777
But instead, the output after the call to ModifySerialized(target) still shows
DataClass: name Foo number 123 (or similar)
--> (DataClass) Target is not modified
However, inside of the function call to ModifySerialized, the console output is what I'd have expected for 'target' (i.e. serialized, 777).
What am I missing? The parameter to the function is a reference, no? The value of the reference should be modified, no? In contrast, if inside ModifySerialized(target) I just set Number=1234, the value would be correctly output to console after the call from Test as expected.
thanks ...
In the ModifySerialized method, you're setting the target parameter to a new instance of DataClass; however, since the parameter is not passed by reference, the testDataClass variable in the Test method still refers to the original instance, which has never been modified. Passing the parameter by reference would produce the behavior you expect.
What am I missing? The parameter to the function is a reference, no? The value of the reference should be modified, no? In contrast, if inside ModifySerialized(target) I just set Number=1234, the value would be correctly output to console after the call from Test as expected.
I think you're confusing two concepts here:
value types vs. reference types
passing parameters by value vs. by reference
Here, the type of the parameter is a reference type, which means that the value of the parameter is a reference; but the reference is passed by value, so assigning a new instance to the parameter doesn't change the original reference.
For more detailed explanations, I suggest you read Jon Skeet's excellent article: Parameter passing in C#
You expect that value of the original variable testDataClass will be changed, but it is not working like this. A copy of its value is passed to method actually and it is chagned during execution, but it can't be reflected to an original variable. For this you should return new value as a method result:
private DataClass ModifySerialized(DataClass target)
{
MemoryStream stream = new MemoryStream();
DataContractSerializer serializer = new DataContractSerializer(typeof(DataClass));
serializer.WriteObject(stream, new DataClass() { Name = "serialized", Number = 777 });
stream.Seek(0, SeekOrigin.Begin);
string sDebug = ASCIIEncoding.ASCII.GetString(stream.GetBuffer());
Console.WriteLine(sDebug);
target = (DataClass)serializer.ReadObject(stream);
Console.WriteLine(target);
return target; // here
}
public Test()
{
DataClass testDataClass = new DataClass() { Name = "Foo", Number = 123 };
testDataClass = ModifySerialized(testDataClass);
Console.WriteLine(testDataClass);
}
Or use ref.
private DataClass ModifySerialized(ref DataClass target)
{
MemoryStream stream = new MemoryStream();
DataContractSerializer serializer = new DataContractSerializer(typeof(DataClass));
serializer.WriteObject(stream, new DataClass() { Name = "serialized", Number = 777 });
stream.Seek(0, SeekOrigin.Begin);
string sDebug = ASCIIEncoding.ASCII.GetString(stream.GetBuffer());
Console.WriteLine(sDebug);
target = (DataClass)serializer.ReadObject(stream);
Console.WriteLine(target);
}
public Test()
{
DataClass testDataClass = new DataClass() { Name = "Foo", Number = 123 };
ModifySerialized(ref testDataClass);
Console.WriteLine(testDataClass);
}
Remember that serializer.ReadObject returns a new object that was just read from the stream. It will replace the object remembered by the target variable within the function, but this operation will not change what the outer variable remembers.
For that, try ModifySerialized(ref DataClass target). But still have in mind that it will not "change the object contents". It will forget the old instance and replace it with new instance.
You can check it in the debugger by "Make Object ID" on both to see which variable refers to object #1 and which to #2 etc
Consider follwing method of copying/cloning an object (all fields are copied into a new object)
public AangepastWerk CloneAdjustedWork(AangepastWerk pAdjustedWork)
{
return new AangepastWerk()
{
AangepastWerkID = pAdjustedWork.AangepastWerkID,
ArbeidsOngeval = pAdjustedWork.ArbeidsOngeval,
DatumCreatie = pAdjustedWork.DatumCreatie,
DatumLaatsteWijziging = pAdjustedWork.DatumLaatsteWijziging,
DatumOngeval = pAdjustedWork.DatumOngeval,
GewijzigdDoor = pAdjustedWork.GewijzigdDoor,
NietErkend = pAdjustedWork.NietErkend,
Stamnummer = pAdjustedWork.Stamnummer,
Verzorging = pAdjustedWork.Verzorging,
VerzorgingId = pAdjustedWork.VerzorgingId
};
}
I have a form that opens up a childform where two objects (2 times the same object of the type mentioned above) is being passed. I open up the form like this:
//my selected Record
Record rec = DateGridAdjustedWorks.ActiveRecord;
AangepastWerk AWorkObject = (AangepastWerk)((DataRecord)rec).DataItem;
AangepastWerk AWorkObjectBackup = _Vm.CloneAdjustedWork(AWorkObject);
WindowModifyAdjustedWork windowForModify = new WindowModifyAdjustedWork(AWorkObject,AWorkObjectBackup, true);
windowForModify.Closing += new CancelEventHandler(OnModifyAWClosing);
windowForModify.ShowDialog();
In that childform I set the first object as DataContext. _adjustedWork and _adjustedWorkCopy are properties of the form
_adjustedWork = pAdjustedWork;
GridAdjustedWork.DataContext = AdjustedWork;
_adjustedWorkCopy = pAdjustedWorkCopy;
The Issue:
In the form i have the ability to alter the object while retaining the original object. the user can see the originalobject, so he has the possibilities to keep track of the changes (request by the user) BUT if i change something in my _adjustedWork (the object that is my datacontext) then my _adjustedWorkCopy (without any actions performed upon it in my code-behind) is changed aswell. My question to you bright minds is: Why does this happen and how do i work around it? What am i missing here (probably something very basic)?
I always do deep cloning with serializing to json.
In example with Servicestack you can:
var json = myObject.ToJson();
var clonedObject = json.FromJson<MyObject>();
return clonedObject;
Maybe this can help.
Found the reason why the Cloned Object recieved the changes as well
public AangepastWerk CloneAdjustedWork(AangepastWerk pAdjustedWork)
{
return new AangepastWerk()
{
AangepastWerkID = pAdjustedWork.AangepastWerkID,
ArbeidsOngeval = pAdjustedWork.ArbeidsOngeval,
DatumCreatie = pAdjustedWork.DatumCreatie,
DatumLaatsteWijziging = pAdjustedWork.DatumLaatsteWijziging,
DatumOngeval = pAdjustedWork.DatumOngeval,
GewijzigdDoor = pAdjustedWork.GewijzigdDoor,
NietErkend = pAdjustedWork.NietErkend,
Stamnummer = pAdjustedWork.Stamnummer,
Verzorging = pAdjustedWork.Verzorging, <------------ Issue lies here
VerzorgingId = pAdjustedWork.VerzorgingId
};
}
pAdjustedWork.Verzorging is an object in this case. I needed to clone this object aswell.I assumed (incorrectly) that cloning as above mentionned would create a new separate 'VerzorgingsObject'.
The solution to my problem is:
MedicalCare_VM mcare_VM = new MedicalCare_VM();
return new AangepastWerk()
{
AangepastWerkID = pAdjustedWork.AangepastWerkID,
ArbeidsOngeval = pAdjustedWork.ArbeidsOngeval,
DatumCreatie = pAdjustedWork.DatumCreatie,
DatumLaatsteWijziging = pAdjustedWork.DatumLaatsteWijziging,
DatumOngeval = pAdjustedWork.DatumOngeval,
GewijzigdDoor = pAdjustedWork.GewijzigdDoor,
NietErkend = pAdjustedWork.NietErkend,
Stamnummer = pAdjustedWork.Stamnummer,
Verzorging = mcare_VM.CloneMedicalCare(pAdjustedWork.Verzorging),
VerzorgingId = pAdjustedWork.VerzorgingId
};
Cloning in:
Verzorging = mcare_VM.CloneMedicalCare(pAdjustedWork.Verzorging)
uses the same logic as mentioned before (copying every field).
I am using LINQ to SQL in an ASP.NET project. While inserting the table I need to convert the values to the particular table object and I need to insert.
For that I created a new constructor in that table with parameter so that I can assign my value to that table object , the assign the functionality is working but while inserting (obj.TS_Questions.InsertOnSubmit(mytableobject);) I get null exception.
my code::
default constructor for my table
public TS_Question()
{
this._TS_Options = new EntitySet<TS_Option>(new Action<TS_Option>(this.attach_TS_Options), new Action<TS_Option>(this.detach_TS_Options));
this._TS_QuestGroups = new EntitySet<TS_QuestGroup>(new Action<TS_QuestGroup>(this.attach_TS_QuestGroups), new Action<TS_QuestGroup>(this.detach_TS_QuestGroups));
this._TS_QuestRecords = new EntitySet<TS_QuestRecord>(new Action<TS_QuestRecord>(this.attach_TS_QuestRecords), new Action<TS_QuestRecord>(this.detach_TS_QuestRecords));
this._TS_Admin = default(EntityRef<TS_Admin>);
this._TS_LevelType = default(EntityRef<TS_LevelType>);
this._TS_OptionTypeLT = default(EntityRef<TS_OptionTypeLT>);
OnCreated();
}
constructor created by me
public TS_Question(Guid Quest_QuestIDBL, string Quest_NameBL, Nullable<Guid> Quest_OptionTypeIDBL, Guid Quest_AdminIDBL, Guid Ques_LevelIDBL, int Quest_TimeBL, int Quest_MarkBL, string Qest_ExplanationBL, Nullable<bool> Qest_IsMultipleAnswerBL)
{
this._TS_Options = new EntitySet<TS_Option>(new Action<TS_Option>(this.attach_TS_Options), new Action<TS_Option>(this.detach_TS_Options));
this._TS_QuestGroups = new EntitySet<TS_QuestGroup>(new Action<TS_QuestGroup>(this.attach_TS_QuestGroups), new Action<TS_QuestGroup>(this.detach_TS_QuestGroups));
this._TS_QuestRecords = new EntitySet<TS_QuestRecord>(new Action<TS_QuestRecord>(this.attach_TS_QuestRecords), new Action<TS_QuestRecord>(this.detach_TS_QuestRecords));
this._TS_Admin = default(EntityRef<TS_Admin>);
this._TS_LevelType = default(EntityRef<TS_LevelType>);
this._TS_OptionTypeLT = default(EntityRef<TS_OptionTypeLT>);
OnCreated();
this._Quest_QuestID = Quest_QuestIDBL;
this._Quest_Name = Quest_NameBL;
if (Quest_OptionTypeIDBL != null)
{
this._Quest_OptionTypeID = Quest_OptionTypeIDBL;
}
this._Quest_AdminID = Quest_AdminIDBL;
this._Ques_LevelID = Ques_LevelIDBL;
this._Quest_Time = Quest_TimeBL;
this._Quest_Mark = Quest_MarkBL;
this._Qest_Explanation = Qest_ExplanationBL;
this._Qest_IsMultipleAnswer = Qest_IsMultipleAnswerBL;
}
Please help me out from this problem
Honestly, I haven't looked too deep, but it looks like that OnCreated is sitting a little far north... You probably want to call it after you're done setting up your variables. Other than that i'd say make sure you're properly initializing everything in the method calling the constructor.
You can call default constructor like this, it works fine for me:
public partial class MyClass
{
public MyClass(string fieldValue1,int fieldValue2)
: this()
{
this.field1= fieldValue1;
this.field2 = fieldValue2;
}
}
If this do the trick, you can read more about using contructors in C# here.