Skip to content

Commit bec9553

Browse files
committed
added support for .net standard 2.0
1 parent e7e6a20 commit bec9553

13 files changed

+676
-55
lines changed

scripts/nuget-pack.bat

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@echo off
22
cd ..
33
md output
4-
cd src\T4Immutable-portable
5-
nuget pack T4Immutable-portable.nuspec -build -properties Configuration=Release -outputdirectory ..\..\output
4+
cd src\T4Immutable-netstd2
5+
nuget pack T4Immutable.nuspec -build -properties Configuration=Release -outputdirectory ..\..\output
66
cd ..\..\scripts

src/T4Immutable-netstd2/Attributes.cs

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using System;
2+
3+
// ReSharper disable RedundantAttributeUsageProperty
4+
5+
// note we can't use features > c#4 since it needs to be compiled by the template
6+
// this means for example no auto-property initializers
7+
// ReSharper disable ConvertToAutoProperty
8+
9+
namespace T4Immutable {
10+
11+
#region For classes
12+
13+
/// <summary>
14+
/// Marks a class so it can be processed by T4Immutable.
15+
/// </summary>
16+
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
17+
public sealed class ImmutableClassAttribute : Attribute {
18+
private ConstructorAccessLevel _constructorAccessLevel = ConstructorAccessLevel.Public;
19+
private ImmutableClassOptions _options = ImmutableClassOptions.None;
20+
private string _preConstructor;
21+
private BuilderAccessLevel _builderAccessLevel = BuilderAccessLevel.Public;
22+
23+
/// <summary>
24+
/// Immutable class generation options.
25+
/// </summary>
26+
public ImmutableClassOptions Options {
27+
get { return _options; }
28+
set { _options = value; }
29+
}
30+
31+
/// <summary>
32+
/// Generated constructor access level (modifier).
33+
/// </summary>
34+
public ConstructorAccessLevel ConstructorAccessLevel {
35+
get { return _constructorAccessLevel; }
36+
set { _constructorAccessLevel = value; }
37+
}
38+
39+
/// <summary>
40+
/// String with code to add before the constructor.
41+
/// </summary>
42+
public string PreConstructor {
43+
get { return _preConstructor; }
44+
set { _preConstructor = value; }
45+
}
46+
47+
/// <summary>
48+
/// Generated builder class access level (modifier).
49+
/// </summary>
50+
public BuilderAccessLevel BuilderAccessLevel {
51+
get { return _builderAccessLevel; }
52+
set { _builderAccessLevel = value; }
53+
}
54+
}
55+
56+
/// <summary>
57+
/// Immutable class generation options for T4Immutable.
58+
/// </summary>
59+
[Flags]
60+
public enum ImmutableClassOptions {
61+
/// <summary>
62+
/// Default.
63+
/// </summary>
64+
None = 0,
65+
66+
/// <summary>
67+
/// Do not generate Equals() implementation or add the IEquatable interface.
68+
/// </summary>
69+
ExcludeEquals = 1 << 0,
70+
71+
/// <summary>
72+
/// Do not generate a GetHashCode() implementation.
73+
/// </summary>
74+
ExcludeGetHashCode = 1 << 1,
75+
76+
/// <summary>
77+
/// Generate operator == and operator !=.
78+
/// </summary>
79+
IncludeOperatorEquals = 1 << 2,
80+
81+
/// <summary>
82+
/// Do not generate a ToString() implementation.
83+
/// </summary>
84+
ExcludeToString = 1 << 3,
85+
86+
/// <summary>
87+
/// Do not generate a With() implementation.
88+
/// </summary>
89+
ExcludeWith = 1 << 4,
90+
91+
/// <summary>
92+
/// Do not generate a constructor.
93+
/// </summary>
94+
ExcludeConstructor = 1 << 5,
95+
96+
/// <summary>
97+
/// Allow the user to define his own constructors.
98+
/// </summary>
99+
AllowCustomConstructors = 1 << 6,
100+
101+
/// <summary>
102+
/// Do not generate a builder class or ImmutableToBuilder() implementation. Implies ExcludeToBuilder.
103+
/// </summary>
104+
ExcludeBuilder = 1 << 7,
105+
106+
/// <summary>
107+
/// Do not generate a ToBuilder() implementation.
108+
/// </summary>
109+
ExcludeToBuilder = 1 << 8,
110+
}
111+
112+
/// <summary>
113+
/// Access level (modifier) for constructors generated by T4Immutable.
114+
/// </summary>
115+
public enum ConstructorAccessLevel {
116+
Public,
117+
Protected,
118+
Internal,
119+
Private,
120+
ProtectedInternal
121+
}
122+
123+
/// <summary>
124+
/// Access level (modifier) for builders generated by T4Immutable.
125+
/// </summary>
126+
public enum BuilderAccessLevel {
127+
Public,
128+
Protected,
129+
Internal,
130+
Private,
131+
ProtectedInternal
132+
}
133+
134+
#endregion
135+
136+
#region For properties
137+
138+
/// <summary>
139+
/// Adds a JetBrains.Annotations.NotNull attribute to the constructor parameter.
140+
/// Also enables a not null precheck implicitely.
141+
/// </summary>
142+
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
143+
public sealed class ConstructorParamNotNullAttribute : Attribute {
144+
}
145+
146+
/// <summary>
147+
/// Generate a not null check at the beginning of the constructor.
148+
/// </summary>
149+
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
150+
public sealed class PreNotNullCheckAttribute : Attribute {
151+
}
152+
153+
/// <summary>
154+
/// Generate a not null check at the end of the constructor.
155+
/// </summary>
156+
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
157+
public sealed class PostNotNullCheckAttribute : Attribute {
158+
}
159+
160+
/// <summary>
161+
/// Marks a property as computed, effectively making T4Immutable ignore it.
162+
/// </summary>
163+
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
164+
public sealed class ComputedPropertyAttribute : Attribute {
165+
}
166+
167+
/// <summary>
168+
/// String with code to add before the constructor parameter.
169+
/// </summary>
170+
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
171+
public sealed class PreConstructorParamAttribute : Attribute {
172+
public string Pre { get; private set; }
173+
174+
public PreConstructorParamAttribute(string pre) {
175+
Pre = pre;
176+
}
177+
}
178+
179+
#endregion
180+
181+
#region Internal
182+
183+
/// <summary>
184+
/// Attribute used internally by T4Immutable to mark generated code. Not for public usage.
185+
/// </summary>
186+
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
187+
public sealed class GeneratedCodeAttribute : Attribute {
188+
}
189+
190+
#endregion
191+
}

src/T4Immutable-netstd2/Helpers.cs

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
7+
namespace T4Immutable {
8+
/// <summary>
9+
/// Collection of helper methods for T4Immutable.
10+
/// </summary>
11+
public static class Helpers {
12+
/// <summary>
13+
/// Check if two objects are equal.
14+
/// </summary>
15+
/// <typeparam name="T">Object type.</typeparam>
16+
/// <param name="a">First object.</param>
17+
/// <param name="b">Second object.</param>
18+
/// <returns>true if they are equal, false otherwise.</returns>
19+
public static bool BasicAreEqual<T>(T a, T b) {
20+
bool aIsNull = ReferenceEquals(a, null), bIsNull = ReferenceEquals(b, null);
21+
if (aIsNull && bIsNull) {
22+
return true;
23+
}
24+
if (aIsNull || bIsNull) {
25+
return false;
26+
}
27+
return a.Equals(b);
28+
}
29+
30+
/// <summary>
31+
/// Check if two objects are equal, plus some special checking for KeyValuePairs and ICollections.
32+
/// </summary>
33+
/// <typeparam name="T">Object type.</typeparam>
34+
/// <param name="a">First object.</param>
35+
/// <param name="b">Second object.</param>
36+
/// <returns>true if they are equal, false otherwise.</returns>
37+
public static bool AreEqual<T>(T a, T b) {
38+
bool aIsNull = ReferenceEquals(a, null), bIsNull = ReferenceEquals(b, null);
39+
if (aIsNull && bIsNull) {
40+
return true;
41+
}
42+
if (aIsNull || bIsNull) {
43+
return false;
44+
}
45+
if (a.Equals(b)) {
46+
return true;
47+
}
48+
49+
// check for the special case of KeyValuePair (items of a dictionary)
50+
var aKvp = KeyValuePairHelper.TryExtractKeyValuePair(a);
51+
if (aKvp != null) {
52+
var bKvp = KeyValuePairHelper.TryExtractKeyValuePair(b);
53+
return AreEqual(aKvp.Item1, bKvp.Item1) && AreEqual(aKvp.Item2, bKvp.Item2);
54+
}
55+
56+
// one extra check for collections
57+
58+
var aCollection = a as ICollection;
59+
var bCollection = b as ICollection;
60+
if ((aCollection == null) || (bCollection == null)) {
61+
return false;
62+
}
63+
64+
if (aCollection.Count != bCollection.Count) {
65+
return false;
66+
}
67+
68+
var aEnum = aCollection.GetEnumerator();
69+
var bEnum = bCollection.GetEnumerator();
70+
71+
while (aEnum.MoveNext()) {
72+
if (!bEnum.MoveNext()) {
73+
return false;
74+
}
75+
object aCurrent = aEnum.Current, bCurrent = bEnum.Current;
76+
if (!AreEqual(aCurrent, bCurrent)) {
77+
return false;
78+
}
79+
}
80+
81+
// all items so far are the same, but does b have one more?
82+
return !bEnum.MoveNext();
83+
}
84+
85+
/// <summary>
86+
/// Gets the hashcode of a single object. If the object is a collection it will make a hashcode of the collection.
87+
/// </summary>
88+
/// <param name="o">Object to make the hashcode for.</param>
89+
/// <returns>A hashcode.</returns>
90+
public static int GetHashCodeForSingleObject(object o) {
91+
if (ReferenceEquals(o, null)) {
92+
return 0;
93+
}
94+
95+
var oCollection = o as ICollection;
96+
if (oCollection == null) {
97+
// check for the special case of KeyValuePair (items of a dictionary)
98+
var kvp = KeyValuePairHelper.TryExtractKeyValuePair(o);
99+
if (kvp != null) {
100+
return GetHashCodeFor(kvp.Item1, kvp.Item2);
101+
}
102+
103+
return o.GetHashCode();
104+
}
105+
106+
// make a hash of the items if it is a collection
107+
var oEnum = oCollection.GetEnumerator();
108+
109+
var list = new List<object>();
110+
while (oEnum.MoveNext()) {
111+
list.Add(oEnum.Current);
112+
}
113+
114+
return GetHashCodeFor(list.ToArray());
115+
}
116+
117+
/// <summary>
118+
/// Returns the hash code of the combination of a series of objects.
119+
/// </summary>
120+
/// <param name="objs">Objects to make the hash code for.</param>
121+
/// <returns>A hashcode.</returns>
122+
public static int GetHashCodeFor(params object[] objs) {
123+
if (objs.Length == 0) {
124+
return 0;
125+
}
126+
127+
const int prime = 486187739;
128+
// overflow is fine
129+
unchecked {
130+
var hash = 17;
131+
foreach (var o in objs) {
132+
hash = hash * prime + GetHashCodeForSingleObject(o);
133+
}
134+
return hash;
135+
}
136+
}
137+
138+
/// <summary>
139+
/// Returns the string representation of an object, null if null or [items] if a collection.
140+
/// </summary>
141+
/// <param name="o"></param>
142+
/// <returns>The string representation.</returns>
143+
public static string ToStringForSingleObject(object o) {
144+
if (ReferenceEquals(o, null)) {
145+
return "null";
146+
}
147+
148+
var oCollection = o as ICollection;
149+
if (oCollection == null) {
150+
// check for the special case of KeyValuePair (items of a dictionary)
151+
var kvp = KeyValuePairHelper.TryExtractKeyValuePair(o);
152+
if (kvp != null) {
153+
return "(" + ToStringForSingleObject(kvp.Item1) + ", " + ToStringForSingleObject(kvp.Item2) + ")";
154+
}
155+
156+
return o.ToString();
157+
}
158+
159+
// make a list of the items if it is a collection
160+
var oEnum = oCollection.GetEnumerator();
161+
162+
var list = new List<object>();
163+
while (oEnum.MoveNext()) {
164+
list.Add(oEnum.Current);
165+
}
166+
167+
var sb = new StringBuilder();
168+
sb.Append("[ ");
169+
// TODO: this could be optimized by not using select and using the append on each object instead
170+
sb.Append(string.Join(", ", list.Select(ToStringForSingleObject)));
171+
sb.Append(" ]");
172+
return sb.ToString();
173+
}
174+
175+
/// <summary>
176+
/// Returns the string representation of an immutable object.
177+
/// </summary>
178+
/// <param name="name">Immutable object type name.</param>
179+
/// <param name="objs">Properties of the immutable objects (name and value).</param>
180+
/// <returns>The string representation.</returns>
181+
public static string ToStringFor(string name, params Tuple<string, object>[] objs) {
182+
var sb = new StringBuilder();
183+
sb.Append(name + " { ");
184+
// TODO: this could be optimized by not using select and using the append on each object instead
185+
sb.Append(string.Join(", ", objs.Select(v => v.Item1 + "=" + ToStringForSingleObject(v.Item2))));
186+
sb.Append(" }");
187+
return sb.ToString();
188+
}
189+
}
190+
}

0 commit comments

Comments
 (0)