Reputation: 1369
Title sums it up. I want to be able to call
Foo f = getStruct<Foo>(..)
and have the method create a new Foo object (it would be a struct), populate it, and return it?
Also, what is the < T > type of constructor called? My google-searches are failing as I'm sure what I should be searching for..
Additionally, I know that all the structs that can be created have a .raw field. I want to be able to populate that .raw field.
This is essentially what I want to do.
public T getStruct<T>(UInt32 sector) {
<T> foo = new <T>;
for (int i=0; i<100; i++) foo.raw[i]=0;
return foo;
}
Where the structs have the form of
StructLayout(LayoutKind.Explicit)]
public unsafe struct RunBlock_t {
[System.Runtime.InteropServices.FieldOffset(0)] public fixed byte raw[512];
[System.Runtime.InteropServices.FieldOffset(0)] public UInt16 run_id;
[System.Runtime.InteropServices.FieldOffset(2)] public UInt16 magic;
[System.Runtime.InteropServices.FieldOffset(510)] public UInt16 checksum;
}
The function passes the .raw pointer and the sector to another function which loads that sector from the disk, then byte by byte copies the contents back into .raw
In this way, I can create an arbitrary struct and populate it from disk quickly and simply. This is not very C# friendly, I know, but there are other external dependencies which require it.
Thnx!
Upvotes: 1
Views: 316
Reputation: 11964
You need to create interface:
public interface IFoo
{
int Raw { get; set; }
}
and all your structures must implement this interface:
public struct Foo:IFoo
{
[System.Runtime.InteropServices.FieldOffset(0)] public fixed byte raw[512];
public int Raw { get{return raw;} set{raw = value;} }
}
And create Generic method:
public static T GetStructs<T>(UInt32 sector) where T : struct, IFoo
{
var foo = new T {Raw = new int[sector]};
for (var i = 0; i < 100; i++)
{
foo.Raw[i] = 0;
}
return foo;
}
To call it use this code:
var foo = getStructs<Foo>(32);
Upvotes: 1
Reputation: 29244
You need a struct factory.
Here I used a combination of interfaces and contained structs. If the fixed byte struct is common, then I create a struct for that, and enclose it in all the structs I define. You can also set an interface to gain access if needed from generic methods.
Initialization is still handled by the Activator
object, and I make sure the raw bytes are always initialized by calling the Initialize()
method in RawBuffer
.
As an example I initialized two different structs, and counted the ticks it takes to complete the operation.
public interface IBlock
{
RawBuffer Raw { get; }
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct RawBuffer
{
public const int Count = 512;
public const int Size = sizeof(byte) * Count;
public fixed byte raw[Count];
public void Initialize()
{
fixed (byte* ptr = raw)
{
for (int i = 0; i < Count; i++)
{
ptr[i] = 0;
}
}
}
}
[StructLayout(LayoutKind.Explicit, Size = RawBuffer.Size)]
public unsafe struct RunBlock_t : IBlock
{
[FieldOffset(0)] public RawBuffer raw;
[FieldOffset(0)] public UInt16 run_id;
[FieldOffset(2)] public UInt16 magic;
[FieldOffset(510)] public UInt16 checksum;
public RunBlock_t(UInt32 sector)
{
raw.Initialize();
run_id = 0;
magic = 0;
checksum = 0;
}
public RawBuffer Raw { get { return raw; } }
}
[StructLayout(LayoutKind.Explicit, Size=RawBuffer.Size)]
public unsafe struct RunBlock_s : IBlock
{
[FieldOffset(0)] public RawBuffer raw;
[FieldOffset(0)] public UInt16 run_id;
[FieldOffset(2)] public UInt32 soup;
[FieldOffset(510)] public UInt16 checksum;
public RunBlock_s(UInt32 sector)
{
raw.Initialize();
run_id = 0;
soup = 0;
checksum = 0;
}
public RawBuffer Raw { get { return raw; } }
}
class Program
{
public static T Factory<T>(UInt32 sector)
where T : struct, IBlock
{
return (T)Activator.CreateInstance(typeof(T), sector);
}
static void Main(string[] args)
{
var sw = Stopwatch.StartNew();
var A = Factory<RunBlock_t>(0x20);
long tic = sw.ElapsedTicks;
Console.WriteLine("Initilized {0} in {1} cycles", A.GetType().Name, tic);
// Initilized RunBlock_t in 1524 cycles
sw = Stopwatch.StartNew();
var B = Factory<RunBlock_s>(0x40);
tic = sw.ElapsedTicks;
Console.WriteLine("Initilized {0} in {1} cycles", B.GetType().Name, tic);
// Initilized RunBlock_s in 722 cycles
}
}
Upvotes: 1
Reputation: 73223
I do not know if this what you are looking for, but you can make a generic method like this:
public static T GetStruct<T>() where T : struct //if T has to be struct
{
return new T();
//or return Activator.CreateInstance<T>();
}
Edit:
I think you're essentially missing the point that the generic T
should be known at compile time if you have to use its properties,fields or methods. If you know your T
will always be Foo
then you need not make your function generic. You could just do:
public Foo getFoo(UInt32 sector)
{
Foo foo = new Foo();
for (int i=0; i<100; i++) foo.raw=0;
return foo;
}
But if you have sub-types for Foo
then there's a point in making the function generic. Like this or so:
public T getStruct<T>(UInt32 sector) where T : Foo
{
T foo = new T();
for (int i=0; i<100; i++) foo.raw=0;
return foo;
}
Now you can call the function by specifying the type you prefer. But for that you will need the raw
function defined on Foo
types. The bottom line is to call any method on an object, it should be known at compile time. Otherwise you will have to use dynamic
keyword which is a bad idea mostly.
Upvotes: 4
Reputation: 21014
Creating one is fairly easy:
public T GetStruct<T>()
{
T myStruct = (T)Activator.CreateInstance<T>();
return myStruct;
}
However, if you want to fill it, you won't be able to in a generic method as you are unaware of the components inside, at least not directly. Unless you use reflection... like this:
using System.Reflection;
public T GetStruct<T>() where T : struct
{
T myStruct = (T)Activator.CreateInstance<T>();
FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo info in infos)
{
//Do something to fill the values;
info.SetValue(myStruct, myValue);
}
return myStruct;
}
Upvotes: 0
Reputation: 19601
If you want to only return struct
's, the adding the generic constraint struct
will mean that you can create a new struct without knowing the type.
If you are going to be returning class
es, then you'll need to add the new()
constraint.
To guarantee that you have a .raw
property on the generic type, you can add an interface as a generic constraint.
public interface IMyInterface
{
int raw { get; set; }
}
public static T GetStruct<T>(UInt32 sector) where T: struct , IMyInterface
{
var obj = new T();
obj.raw = 0;
return obj;
}
The obj
will know, due to the IMyInterface
constraint, that the raw
property is guaranteed to be on the generic type.
Upvotes: 0