Context
The context appears to be irrelevant to my question, but anyway let me explain it a bit:
I am using SWIG to generate code that glues C++ and C# together. I have a struct in C++ like this:
struct Student {
char* name;
double score1;
double score2;
double score3;
double score4;
};
and I generate the glue code with: swig -c++ -csharp -O mylib.i so that I can work with it in C#.
Question
The C# code is generated as follows:
public class Student : global::System.IDisposable {
private global::System.Runtime.InteropServices.HandleRef swigCPtr;
protected bool swigCMemOwn;
internal Student (global::System.IntPtr cPtr, bool cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
}
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(Student obj) {
return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
}
~Student () {
//...
What confuses me is its constructor,
swigCMemOwn = cMemoryOwn;
swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
This two lines take 100+ ns to execute. I examined the internals of HandleRef, it is rather straightforward (if I am not mistaken):
public readonly struct HandleRef
{
// ! Do not add or rearrange fields as the EE depends on this layout.
//------------------------------------------------------------------
private readonly object? _wrapper;
private readonly IntPtr _handle;
//------------------------------------------------------------------
public HandleRef(object? wrapper, IntPtr handle)
{
_wrapper = wrapper;
_handle = handle;
}
public object? Wrapper => _wrapper;
public IntPtr Handle => _handle;
public static explicit operator IntPtr(HandleRef value) => value._handle;
public static IntPtr ToIntPtr(HandleRef value) => value._handle;
}
How come this simple value assignment takes so long? My expectation is that it should take no more than a few ns.
Related
Im building an Add-In for Outlook where I copy all exchange users from the global address list to the local contacts.
The problem is I want transfer the picture of the exchange user too, but exchUser.GetPicture() returns a stdole.stdPicture and I have not yet found a working solution to download or convert it into an image/jpg/...
Here the code to get the exchange User from the global address list:
private void EnumerateGAL()
{
Outlook.AddressList gal = Application.Session.GetGlobalAddressList();
if (gal != null)
{
for (int i = 1; i <= gal.AddressEntries.Count - 1; i++)
{
Outlook.AddressEntry addrEntry = gal.AddressEntries[i];
Outlook.ExchangeUser exchUser = addrEntry.GetExchangeUser();
if (addrEntry.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry
&& exchUser.CompanyName == "")
{
CreateContact(exchUser);
//exchUser.GetPicture() returns stdole.stdPicture
}
}
}
return;
}
The closest solution I found, was a conversion of stdole.IPictureDisp which returns a bitmap but IPuctureDisp and stdPicture isn´t the same as I read somewhere.
public static System.Drawing.Image ConvertPicture(stdole.IPictureDisp image)
{
int type = image.Type;
if (type == 1)
{
IntPtr hPal = (IntPtr)image.hPal;
return Image.FromHbitmap((IntPtr)image.Handle, hPal);
}
return null;
}
In the end I need to download the picture because I can only upload a picture to a contact with a path.
So, is there a way to download a stdPicture or convert it to be able to download it?
There are four main ways to get the job done.
The "traditional" approach is to use the GetIPictureDispFromPicture and GetPictureFromIPicture methods in the System.Windows.Forms.AxHost class. These are both protected members of the class, so you can't use them externally. For this reason, it is common to subclass the AxHost class and expose public methods that internally call the base class protected methods. This approach allows you to convert in both directions:
internal class AxHostConverter : AxHost
{
private AxHostConverter() : base("") { }
static public stdole.IPictureDisp ImageToPictureDisp(Image image)
{
return (stdole.IPictureDisp)GetIPictureDispFromPicture(image);
}
static public Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
{
return GetPictureFromIPicture(pictureDisp);
}
}
Your second option is to use OleLoadPicture or OleCreatePictureIndirect. There's an old support article on the topic here. OleLoadPicture creates a new picture object and initializes it from the contents of a stream.
internal class OleCreateConverter
{
[DllImport("oleaut32.dll", EntryPoint = "OleCreatePictureIndirect",
CharSet = CharSet.Ansi, ExactSpelling = true, PreserveSig = true)]
private static extern int OleCreatePictureIndirect(
[In] PictDescBitmap pictdesc, ref Guid iid, bool fOwn,
[MarshalAs(UnmanagedType.Interface)] out object ppVoid);
const short _PictureTypeBitmap = 1;
[StructLayout(LayoutKind.Sequential)]
internal class PictDescBitmap
{
internal int cbSizeOfStruct = Marshal.SizeOf(typeof(PictDescBitmap));
internal int pictureType = _PictureTypeBitmap;
internal IntPtr hBitmap = IntPtr.Zero;
internal IntPtr hPalette = IntPtr.Zero;
internal int unused = 0;
internal PictDescBitmap(Bitmap bitmap)
{
this.hBitmap = bitmap.GetHbitmap();
}
}
public static stdole.IPictureDisp ImageToPictureDisp(Image image)
{
if (image == null || !(image is Bitmap))
{
return null;
}
PictDescBitmap pictDescBitmap = new PictDescBitmap((Bitmap)image);
object ppVoid = null;
Guid iPictureDispGuid = typeof(stdole.IPictureDisp).GUID;
OleCreatePictureIndirect(pictDescBitmap, ref iPictureDispGuid, true, out ppVoid);
stdole.IPictureDisp picture = (stdole.IPictureDisp)ppVoid;
return picture;
}
public static Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
{
Image image = null;
if (pictureDisp != null && pictureDisp.Type == _PictureTypeBitmap)
{
IntPtr paletteHandle = new IntPtr(pictureDisp.hPal);
IntPtr bitmapHandle = new IntPtr(pictureDisp.Handle);
image = Image.FromHbitmap(bitmapHandle, paletteHandle);
}
return image;
}
}
Your third option is to use the VB6 compatibility library, documented here. To use this, you'll need to add a reference to Microsoft.VisualBasic.Compatibility.dll, which is listed on the .NET tab of the Add References dialog (it resides in the GAC). Then, you can use the ImageToIPictureDisp and IPictureDispToImage methods in the Support class. This is obviously by far the simplest approach, although it does pull in the VB6 compatibility DLL. Internally, the VB6 compatibility code looks a lot like the second option above – using OleCreatePictureIndirect.
using Microsoft.VisualBasic.Compatibility.VB6;
internal class VB6CompatibilityConverter
{
public static stdole.IPictureDisp ImageToPictureDisp(Image image)
{
return (stdole.IPictureDisp)Support.ImageToIPictureDisp(image);
}
public static Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
{
return Support.IPictureDispToImage(pictureDisp);
}
}
Finally, you can implement IPictureDisp and IPicture yourself. This is fine if you just want to convert from an Image to an IPictureDisp, but doesn't help you converting in the other direction. The implementation below relies on the Image actually being a derived Bitmap type, because we call Bitmap.GetHbitmap internally. If you want to keep the support to the generic Image type, you'll have to do a lot more work to p/invoke to a bunch of undocumented GDI methods instead
internal class PictureDispConverter
{
public static stdole.IPictureDisp BitmapToPictureDisp(Bitmap bitmap)
{
return new PictureDispImpl(bitmap);
}
public static Image PictureDispToBitmap(stdole.IPictureDisp pictureDisp)
{
// TODO
return null;
}
}
internal class PictureDispImpl : stdole.IPictureDisp, stdole.IPicture
{
#region Init
[DllImport("gdi32.dll")]
static extern void DeleteObject(IntPtr handle);
private Bitmap _image;
private IntPtr _handle;
public PictureDispImpl(Bitmap image)
{
_image = image;
}
~PictureDispImpl()
{
if (_handle != IntPtr.Zero)
{
DeleteObject(_handle);
}
}
#endregion
#region IPictureDisp Members
public int Width
{
get { return _image.Width; }
}
public int Height
{
get { return _image.Height; }
}
public short Type
{
get { return 1; }
}
public int Handle
{
get
{
if (_handle == IntPtr.Zero)
{
_handle = _image.GetHbitmap();
}
return _handle.ToInt32();
}
}
public int hPal
{
get { return 0; }
set { }
}
public void Render(
int hdc, int x, int y, int cx, int cy, int xSrc, int ySrc, int cxSrc, int cySrc, IntPtr prcWBounds)
{
Graphics graphics = Graphics.FromHdc(new IntPtr(hdc));
graphics.DrawImage(
_image, new Rectangle(x, y, cx, cy), xSrc, ySrc, cxSrc, cySrc, GraphicsUnit.Pixel);
}
#endregion
#region IPicture Members
public int Attributes
{
get { return 0; }
}
public int CurDC
{
get { return 0; }
}
public bool KeepOriginalFormat
{
get { return false; }
set { }
}
public void PictureChanged()
{
}
public void SaveAsFile(IntPtr pstm, bool fSaveMemCopy, out int pcbSize)
{
pcbSize = 0;
}
public void SelectPicture(int hdcIn, out int phdcOut, out int phbmpOut)
{
phdcOut = 0;
phbmpOut = 0;
}
public void SetHdc(int hdc)
{
}
#endregion
}
Attempting to learn and use a bit more about class serialization to XML.
I can do basic serialisation.
In the following case I have a nested class structure and would like to get it output in its implemented hierarchy to a file in XML standard. The same for deserializing if it there are any caveats.
I had a good search but couldn't understand how to do it.
I know some bits below are not 100% safe but I'm more worried about the serialization functionality. The serializer class extends the Serialize() to any of my classes.
Your help is much appreciated.
public class SysConfigs
{
public SysConfigs() { }
public struct DeadVolumes
{
public struct Valve
{
public uint V1; //valve 1 dead volume in uL
public uint V2; //valve 2 deal volume
public uint V3;
public uint V4;
public uint V5p0;
public uint V5p1;
public uint V5p2;
public uint V5p3;
public uint V5p4;
public uint V5p5;
public uint v5p6;
public uint V5p7;
public uint V5p8;
public uint V5p9;
public uint V5p10;
public uint V5p11;
public uint V5p12;
public uint V5p13;
public uint V5p14;
public uint V6;
}
public struct Mixer
{
public uint M1p1;
public uint M1p2;
public uint M1p3;
public uint M2p1;
public uint M2p2;
public uint M2p3;
}
}
}
public static class Serializer
{
public static void Serialize(this Object obj, string filePath)
{
// Creates an instance of the XmlSerializer class;
// specifies the type of object to be deserialized.
XmlSerializer _xml_serializer = new XmlSerializer(obj.GetType());
XmlWriter _xml_textwriter = XmlWriter.Create(filePath);
try
{
_xml_serializer.Serialize(_xml_textwriter, obj);
}
catch (Exception ex)
{
throw ex;
}
_xml_textwriter.Close();
// If the XML document has been altered with unknown
// nodes or attributes, handles them with the
// UnknownNode and UnknownAttribute events.
_xml_serializer.UnknownNode += new XmlNodeEventHandler(serializer_UnknownNode);
_xml_serializer.UnknownAttribute += new XmlAttributeEventHandler(serializer_UnknownAttribute);
}
// this is where I am testing the my serialization implementation from
private void button1_Click(object sender, EventArgs e)
{
SysConfigs stuff = new SysConfigs();
stuff.Serialize("myfile.xml");
}
The example above produces an XML without contents - that is understood.
<?xml version="1.0" encoding="utf-8"?><SysConfigs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
I would like to understand what is required, given the example above, to achieve the following XML program output:
<SysConfigs>
<DeadVolumes>
<Valve>
<V1>123</V1>
<V2>123</V2>
<V3>123</V3>
..etc..
</Valve>
<Mixer>
<M1p1>1</M1p1>
<M1p2>1</M1p2>
<M1p3>1</M1p3>
..etc..
</Mixer>
</DeadVolumes>
</SysConfigs>
The problem is that your SysConfigs class has no members. So there's nothing to serialize. There's also no need to make the structs nested, or to use structs in the first place. So instead just create a series of classes:
public class Valve
{
public uint V1;
// ...
public uint V6;
}
public class Mixer
{
public uint M1p1;
// ...
public uint M1p3;
}
Then create the container class containing the proper members:
public class DeadVolume
{
public Valve Valve { get;set; }
public Mixer Mixer { get;set; }
}
Then add a property to the SysConfigs class:
public class SysConfigs
{
public DeadVolume DeadVolumes { get; set; }
}
Now you can instantiate and populate a SysConfigs instance and serialize it:
var config = new SysConfigs
{
DeadVolumes = new DeadVolume
{
Valve = new Valve
{
V1 = 42,
// ...
},
Mixer = new Mixer
{
M1p1 = 43,
// ..
}
}
};
I have been working with an interface which read measurement data from a sensor and use a library written in C++ to analyse that data.
The function is just about the following:
Set measurement parameters on the C++ library
Get data from the sensor (1200 measurements alltogether)
Write data to C++ library
Process all 1200 measurements in C++ library
Read results from C++ library
Calling this C++ library from C#-code is previously dealt with this question: Calling unmanaged C++ library (dll) from C# creates an access violation error (0xc0000005).
Now it seems, that the C++ library either
doesn't get data correctly
is not able to hold data
is not able to properly return results to my C#-code.
Bad thing is, that I'm not able to debug this C++ library.
What is wrong with my code?
1) Setting the measurement parameters
namespace PdWaveApi
{
[StructLayout(LayoutKind.Sequential)]
public struct PDDataInfo
{
public int nPings;
public int nDataRate;
public int nSamples;
public float fFrequency;
public float fBeamAngle;
public int nInstrument;
public int nCoordSystem;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public short[] hBeamToXYZ;
public short hWaveT1;
// Constructor
public static PDDataInfo Create()
{
PDDataInfo DataStruct = new PDDataInfo();
DataStruct.hBeamToXYZ = new short[9];
return DataStruct;
}
}
}
public class PdWaveBaseLWrapper
{
[DllImport("PdWaveBase.dll", EntryPoint = "PDSetInstrumentConfig")]
public static extern int PDSetInstrumentConfig(ref PDDataInfo pDataInfo);
}
public void SetInstrumentConfiguration()
{
PdWaveApi.PDDataInfo InstrumentConfiguration = new PdWaveApi.PDDataInfo();
.................
Initializing the InstrumentConfiguration structure
...............
PdWaveBaseLWrapper.PDSetInstrumentConfig(ref InstrumentConfiguration);
}
3) Reading data from sensor and writing data to C++ library
namespace PdWaveApi
{
[StructLayout(LayoutKind.Sequential)]
public struct PDWaveSample
{
[MarshalAs(UnmanagedType.I1)]
public bool Valid;
public float fPressure;
public float fDistance;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.PD_MAX_WAVEBEAMS)]
public float[] fVel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.PD_MAX_WAVEBEAMS)]
public ushort[] nAmp;
// Constructor
public static PDWaveSample Create()
{
PDWaveSample DataStruct = new PDWaveSample();
DataStruct.fVel = new float[Constants.PD_MAX_WAVEBEAMS];
DataStruct.nAmp = new ushort[Constants.PD_MAX_WAVEBEAMS];
return DataStruct;
}
}
}
public class PdWaveBaseLWrapper
{
[DllImport("PdWaveBase.dll", EntryPoint = "PDSetWaveSample")]
public static extern int PDSetWaveSample(ref PDWaveSample pWaveSample);
}
namespace SensorInterface
{
public partial class frmSensorInterface : Form
{
public PdWaveApi.PDWaveSample WaveSampleData = PdWaveApi.PDWaveSample.Create();
private void OnNewData(object sender, OnNewDataEvent e)
{
ReadWaveSample(ref WaveSampleData);
SetWaveSample(ref WaveSampleData);
}
public void ReadWaveSample(ref PdWaveApi.PDWaveSample WaveSampleData)
{
DateTime MeasurementTimeStamp;
float[] dVel = new float[4];
float dTemperature = new float();
float dPitch = new float();
float dRoll = new float();
float dHeading = new float();
float dPressure = new float();
short[] sAmp = new short[4];
// Read some of the latest data from the control
GetVelocity(ref dVel[0], ref dVel[1], ref dVel[2], ref dVel[3]);
GetAmplitude(ref sAmp[0], ref sAmp[1], ref sAmp[2], ref sAmp[2]);
..............
// Set other data to the structure
}
public void SetWaveSample(ref PdWaveApi.PDWaveSample WaveSampleData)
{
PdWaveBaseLWrapper.PDSetWaveSample(ref WaveSampleData);
}
}
}
4) Process all 1200 measurements in C++ library
[StructLayout(LayoutKind.Sequential)]
public struct PDWaveBurst
{
[MarshalAs(UnmanagedType.ByValArray , SizeConst = Constants.PD_MAX_WAVEMEAS_AST)]
public float[] fST;
public float fWinFloor;
public float fWinCeil;
[MarshalAs(UnmanagedType.I1)]
public bool bUseWindow;
[MarshalAs(UnmanagedType.I1)]
public bool bSTOk;
[MarshalAs(UnmanagedType.I1)]
public bool bGetRawAST;
[MarshalAs(UnmanagedType.I1)]
public bool bValidBurst;
public static PDWaveBurst Create()
{
PDWaveBurst DataStruct = new PDWaveBurst();
DataStruct.fST = new float[Constants.PD_MAX_WAVEMEAS_AST];
return DataStruct;
}
}
[DllImport("PdWaveBase.dll", EntryPoint = "PDPreProcess")]
public static extern int PDPreProcess(int nSample, ref PDWaveBurst pWaveBurst);
[DllImport("PdWaveBase.dll", EntryPoint = "PDProcessReturnInt")]
public static extern int PDProcessReturnInt();
public void PreprocessBurstData(int nSamples)
{
PdWaveApi.PDWaveBurst WaveBurstData = PdWaveApi.PDWaveBurst.Create();
WaveBurstData.fST = new float[4096];
WaveBurstData.fWinFloor = (float)1.25;
WaveBurstData.fWinCeil = 2;
WaveBurstData.bUseWindow = false;
WaveBurstData.bSTOk = false;
WaveBurstData.bGetRawAST = false;
WaveBurstData.bValidBurst = false;
PdWaveBaseLWrapper.PDPreProcess(nSamples, ref WaveBurstData);
}
public void ProcessData()
{
int ProcessError = PdWaveBaseLWrapper.PDProcessReturnInt();
}
5) Read results from C++ library
[StructLayout(LayoutKind.Sequential)]
public struct PDWavePar {
public float fTm02;
public float fTp;
public float fDirTp;
public float fSprTp;
public float fMainDir;
public float fUI;
public float fHm0;
public float fH3;
public float fT3;
public float fH10;
public float fT10;
public float fHmax;
public float fTmax;
public float fTz;
public float fMeanPres;
public int nNumNoDet;
public int nNumBadDet;
public int nErrCode;
public int nSpectrum;
public float fMeanAST;
}
[DllImport("PdWaveBase.dll", EntryPoint = "PDGetWavePar")]
public static extern int PDGetWavePar(ref PDWavePar pwWavePar);
public void GetOutput()
{
PdWaveApi.PDWavePar WaveParameters = new PdWaveApi.PDWavePar();
PdWaveBaseLWrapper.PDGetWavePar(ref WaveParameters);
}
So, as conclusion:
What should I change in my code
- to pass data correctly to unmanaged dll
- to have dll hold and process the data in its' internal structures
- to read results correctly from unmanaged code to my C# program?
(Sorry about the length of my question.)
The final solution to my problem was to change the alignment to 1 byte, like this:
C# struct definitions:
From:
[StructLayout(LayoutKind.Sequential)]
to
[StructLayout(LayoutKind.Sequential, Pack=1)]
This has been discussed at least here:
Calling C++ metro dll from c# metro app and returning filled structures
I am currently working on a third party dll, and randomly when the delegate OnReceive is invoked I get the following error:
CallbackOnCollectedDelegate was detected
I read that GC.Collect() can solve the problem using static but also not as, I have hours and trying every way CallbackOnCollectedDelegate get the error, please help...
namespace Interfaz
{
class TMPDlg:
{
public CTMIF m_objTMPInterface;
public uint m_dwLocalIP;
public ushort m_nPort;
public byte m_nSubNet;
public uint m_nRadioID;
public uint m_nIndex;
public uint m_dwMobileID;
public int nLength;
public string mensaje_destino;
public string mensaje_recibido;
public TMPDlg()
{
m_objTMPInterface = null;
}
unsafe public void OnReceive(ushort wOpcode, System.IntPtr pbPayload, uint dwSize, uint dwLocalIP)
{
TMReceive(wOpcode, (byte*)pbPayload, dwSize, dwLocalIP);
}
unsafe public void TMReceive(ushort wOpcode, byte * pbPayload, uint dwSize, uint dwLocalIP)
{
// Some Work....
}
public void Send_PrivateMsg(string textBoxMensaje, string labelID)
{
m_nRadioID = uint.Parse(labelID);
mensaje_destino = textBoxMensaje;
nLength = textBoxMensaje.Length;
m_objTMPInterface.SendPrivateMsg(m_nRadioID, mensaje_destino, nLength, 0);
}
public void conect_master(ushort port, string ip)
{
m_objTMPInterface = new CTMIF();
m_dwLocalIP = (uint)IPAddressToNumber(ip);
ADKCALLBACK myOnReceive = new ADKCALLBACK(OnReceive);
m_objTMPInterface.SetCallBackFn(myOnReceive);
//m_objTMPInterface.SetCallBackFn(OnReceive);
m_objTMPInterface.OpenSocket(m_dwLocalIP, port, m_dwMobileID, 10)<
}
Presumably this part is the problem?
ADKCALLBACK myOnReceive = new ADKCALLBACK(OnReceive);
m_objTMPInterface.SetCallBackFn(myOnReceive);
If you have an instance variable of type ADKCALLBACK, then so long as your instance isn't garbage collected before (or while) the callback function is executing, you should be okay. What controls your instance's lifetime?
class TMPDlg
{
// Instance variable to protect from garbage collection
private readonly ADKCALLBACK receiveCallback;
public TMPDlg()
{
receiveCallback = myOnReceive;
}
...
public void ConnectMaster(ushort port, string ip)
{
...
m_objTMPInterface.SetCallBackFn(receiveCallback);
...
}
}
(As an aside, your naming could be improved significantly, and you should avoid having public fields.)
I'm sure I've seen somewhere that I can do the following by using an attribute above my Init() method, that tells the compiler that the Init() method must only be called from the constructor, thus allowing the readonly field to be set. I forgot what the attribute is called though, and I can't seem to find it on google.
public class Class
{
private readonly int readonlyField;
public Class()
{
Init();
}
// Attribute here that tells the compiler that this method must be called only from a constructor
private void Init()
{
readonlyField = 1;
}
}
Rob's answer is the way to do it, in my book. If you need to initialize multiple fields you can do it using out parameters:
public class Class
{
private readonly int readonlyField1;
private readonly int readonlyField2;
public Class()
{
Init(out readonlyField1, out readonlyField2);
}
protected virtual void Init(out int field1, out int field2)
{
field1 = 1;
field2 = 2;
}
}
Personally I find this makes sense in certain scenarios, such as when you want your fields to be readonly but you also want to be able to set them differently in a derived class (without having to chain a ton of parameters through some protected constructor). But maybe that's just me.
Instead of using an Initialize method, how about inheriting a basic constructor through all your other constructors.
i.e.
public class MyClass
{
readonly int field1;
readonly double field2;
public MyClass(int field1, double field2)
{
//put whatever initialization logic you need here...
field1 = 10;
field2 = 30.2;
}
public MyClass(int field1, double field2p1, double field2p2)
: this(field1, (field2p1 + field2p2))
{
//put anything extra in here
}
}
This may be a little late to reach the original person in need, but it seems like this will cleanly solve the problem... Without the need to use any sort of nasty reflection or out parameters.
The only solution I can think of is to return the value from the Init() method that the readonly field needs to be assigned:
public class Class
{
private readonly int readonlyField;
public Class()
{
readonlyField = Init();
}
private int Init()
{
return 1;
}
}
Jared is right; this is not possible. The workarounds I can think of are:
Initialize the field in the declaration.
Initialize the field in the constructor (Manually inline your Init method).
Assign the field to a value returned by a method, e.g.: _myField = GetInitialMyFieldValue();
Pass the field to the Init method, with the out modifier. This may be useful if you have many fields to initialize, which are dependent on constructor parameters. E.g.
private readonly int _x;
private readonly string _y;
private void Init(int someConstructorParam, out int x, out string y){ .. }
public Class(int someConstructorParam)
{
Init(someConstructorParam, out _x, out _y);
}
This cannot be done. Fields which are tagged with readonly can only be set from the constructor
What i ended up doing in current tech (C# 7.x) is use the value tuple system:
public class MyClass
{
private readonly int x;
private readonly int y;
private readonly int z;
public MyClass(int x)
{
this.x = x;
(y, z) = InitYandZ();
}
private (int, int) InitYandZ()
{
return (5, 10);
}
}
Not the cleanest either, But seems cleaner to me.
C# compiler only allows you to set readonly fields if you're initializing them inline:
private readonly int readonlyField = 1;
or from the constructor:
public Class()
{
readonlyField = 1;
}
How about an initialized property with a getter only (as of C# 6.0)?
private int MyProperty { get; } = 0;
I know this is late, but what about using a class as a return value instead of using out params if there is a need to initialize multiple fields, are there any drawacks? Imho this is more convenient and more readable than using nested constructors.
public class MyInitClass
{
public int Field1 { get; set; }
public int Field2 { get; set; }
}
public class Class
{
private readonly int readonlyField1;
private readonly int readonlyField2;
public Class()
{
var init = Init();
readonlyField1 = init.Field1;
readonlyField2 = init.Field2;
}
private MyInitClass Init()
{
return new MyInitClass() { Field1 = 1, Field2 = 2 };
}
}
I think it works if use Reflection. Actually this works for me:
public class Class
{
private readonly int readonlyField;
public int MyField()
{
return readonlyField;
}
public Class()
{
readonlyField = 9;
}
}
and
static void Main(string[] args)
{
Class classObj = new Class();
Console.WriteLine(classObj.MyField());//9
Misc.SetVariableyByName(classObj, "readonlyField", 20);//20
Console.WriteLine(classObj.MyField());
}
this is SetVariableByName():
public static b
ool SetVariableyByName(object obj, string var_name, object value)
{
FieldInfo info = obj.GetType().GetField(var_name, BindingFlags.NonPublic| BindingFlags.Instance);
if (info == null)
return false;
/* ELSE */
info.SetValue(obj, value);
return true;
}
the only thing is that readonlyField is public not private. I know that you can edit a private field, but am not sure why its not working for me!