remove the freeze API

pull/654/head
Jan Tattermusch 10 years ago
parent 74810c6ae3
commit 3783d9a8ad
  1. 1
      Makefile.am
  2. 56
      csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs
  3. 36
      csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs
  4. 17
      csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
  5. 34
      csharp/src/Google.Protobuf/Collections/MapField.cs
  6. 29
      csharp/src/Google.Protobuf/Collections/RepeatedField.cs
  7. 58
      csharp/src/Google.Protobuf/Freezable.cs
  8. 1
      csharp/src/Google.Protobuf/Google.Protobuf.csproj
  9. 34
      csharp/src/Google.Protobuf/IMessage.cs
  10. 2
      src/google/protobuf/compiler/csharp/csharp_map_field.cc
  11. 28
      src/google/protobuf/compiler/csharp/csharp_message.cc
  12. 4
      src/google/protobuf/compiler/csharp/csharp_message_field.cc
  13. 6
      src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
  14. 2
      src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
  15. 2
      src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
  16. 2
      src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
  17. 2
      src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc

@ -99,7 +99,6 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/Collections/RepeatedField.cs \
csharp/src/Google.Protobuf/FieldCodec.cs \
csharp/src/Google.Protobuf/FrameworkPortability.cs \
csharp/src/Google.Protobuf/Freezable.cs \
csharp/src/Google.Protobuf/Google.Protobuf.csproj \
csharp/src/Google.Protobuf/Google.Protobuf.nuspec \
csharp/src/Google.Protobuf/IMessage.cs \

@ -45,53 +45,6 @@ namespace Google.Protobuf.Collections
/// </summary>
public class MapFieldTest
{
// Protobuf-specific tests
[Test]
public void Freeze_FreezesMessages()
{
var message = new ForeignMessage { C = 20 };
var map = new MapField<string, ForeignMessage> { { "x", message } };
map.Freeze();
Assert.IsTrue(message.IsFrozen);
}
[Test]
public void Freeze_Idempotent()
{
var message = new ForeignMessage { C = 20 };
var map = new MapField<string, ForeignMessage> { { "x", message } };
Assert.IsFalse(map.IsFrozen);
map.Freeze();
Assert.IsTrue(message.IsFrozen);
map.Freeze();
Assert.IsTrue(message.IsFrozen);
}
[Test]
public void Freeze_PreventsMutation()
{
var map = new MapField<string, string>();
map.Freeze();
Assert.IsTrue(map.IsFrozen);
Assert.IsTrue(map.IsReadOnly);
ICollection<KeyValuePair<string, string>> collection = map;
Assert.Throws<InvalidOperationException>(() => map["x"] = "y");
Assert.Throws<InvalidOperationException>(() => map.Add("x", "y"));
Assert.Throws<InvalidOperationException>(() => map.Remove("x"));
Assert.Throws<InvalidOperationException>(() => map.Clear());
Assert.Throws<InvalidOperationException>(() => collection.Add(NewKeyValuePair("x", "y")));
Assert.Throws<InvalidOperationException>(() => collection.Remove(NewKeyValuePair("x", "y")));
}
[Test]
public void Clone_ReturnsNonFrozen()
{
var map = new MapField<string, string>();
map.Freeze();
var clone = map.Clone();
clone.Add("x", "y");
}
[Test]
public void Clone_ClonesMessages()
{
@ -422,10 +375,6 @@ namespace Google.Protobuf.Collections
dictionary.Remove("x");
Assert.AreEqual(0, dictionary.Count);
Assert.Throws<ArgumentNullException>(() => dictionary.Remove(null));
map.Freeze();
// Call should fail even though it clearly doesn't contain 5 as a key.
Assert.Throws<InvalidOperationException>(() => dictionary.Remove(5));
}
[Test]
@ -449,8 +398,6 @@ namespace Google.Protobuf.Collections
var map = new MapField<string, string> { { "x", "y" } };
IDictionary dictionary = map;
Assert.IsFalse(dictionary.IsFixedSize);
map.Freeze();
Assert.IsTrue(dictionary.IsFixedSize);
}
[Test]
@ -504,9 +451,6 @@ namespace Google.Protobuf.Collections
Assert.Throws<InvalidCastException>(() => dictionary["x"] = 5);
Assert.Throws<ArgumentNullException>(() => dictionary[null] = "z");
Assert.Throws<ArgumentNullException>(() => dictionary["x"] = null);
map.Freeze();
// Note: Not InvalidOperationException.
Assert.Throws<NotSupportedException>(() => dictionary["a"] = "c");
}
[Test]

@ -193,44 +193,10 @@ namespace Google.Protobuf.Collections
Assert.Throws<ArgumentOutOfRangeException>(() => list[2] = "bad");
}
[Test]
public void Freeze_FreezesElements()
{
var list = new RepeatedField<TestAllTypes> { new TestAllTypes() };
Assert.IsFalse(list[0].IsFrozen);
list.Freeze();
Assert.IsTrue(list[0].IsFrozen);
}
[Test]
public void Freeze_PreventsMutations()
{
var list = new RepeatedField<int> { 0 };
list.Freeze();
Assert.Throws<InvalidOperationException>(() => list.Add(1));
Assert.Throws<InvalidOperationException>(() => list[0] = 1);
Assert.Throws<InvalidOperationException>(() => list.Clear());
Assert.Throws<InvalidOperationException>(() => list.RemoveAt(0));
Assert.Throws<InvalidOperationException>(() => list.Remove(0));
Assert.Throws<InvalidOperationException>(() => list.Insert(0, 0));
}
[Test]
public void Freeze_ReportsFrozen()
{
var list = new RepeatedField<int> { 0 };
Assert.IsFalse(list.IsFrozen);
Assert.IsFalse(list.IsReadOnly);
list.Freeze();
Assert.IsTrue(list.IsFrozen);
Assert.IsTrue(list.IsReadOnly);
}
[Test]
public void Clone_ReturnsMutable()
{
var list = new RepeatedField<int> { 0 };
list.Freeze();
var clone = list.Clone();
clone[0] = 1;
}
@ -585,8 +551,6 @@ namespace Google.Protobuf.Collections
var field = new RepeatedField<string> { "first", "second" };
IList list = field;
Assert.IsFalse(list.IsFixedSize);
field.Freeze();
Assert.IsTrue(list.IsFixedSize);
}
[Test]

@ -503,23 +503,6 @@ namespace Google.Protobuf
Assert.AreNotEqual(original, clone);
}
[Test]
public void Freeze()
{
var frozen = new TestAllTypes();
frozen.Freeze();
Assert.IsTrue(frozen.IsFrozen);
Assert.Throws<InvalidOperationException>(() => frozen.ClearOneofField());
Assert.Throws<InvalidOperationException>(() => frozen.SingleInt32 = 0);
Assert.Throws<InvalidOperationException>(() => frozen.SingleNestedMessage = null);
Assert.Throws<InvalidOperationException>(() => frozen.SingleNestedEnum = 0);
Assert.Throws<InvalidOperationException>(() => frozen.OneofString = null);
Assert.Throws<InvalidOperationException>(() => frozen.OneofUint32 = 0U);
Assert.Throws<InvalidOperationException>(() => frozen.RepeatedDouble.Add(0.0));
Assert.Throws<InvalidOperationException>(() => frozen.RepeatedNestedMessage.Add(new TestAllTypes.Types.NestedMessage()));
}
[Test]
public void OneofProperties()
{

@ -50,7 +50,7 @@ namespace Google.Protobuf.Collections
/// </remarks>
/// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>
/// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary
{
// TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
private readonly bool allowNullValues;
@ -119,7 +119,6 @@ namespace Google.Protobuf.Collections
public bool Remove(TKey key)
{
this.CheckMutable();
ThrowHelper.ThrowIfNull(key, "key");
LinkedListNode<KeyValuePair<TKey, TValue>> node;
if (map.TryGetValue(key, out node))
@ -169,7 +168,6 @@ namespace Google.Protobuf.Collections
{
ThrowHelper.ThrowIfNull(value, "value");
}
this.CheckMutable();
LinkedListNode<KeyValuePair<TKey, TValue>> node;
var pair = new KeyValuePair<TKey, TValue>(key, value);
if (map.TryGetValue(key, out node))
@ -214,7 +212,6 @@ namespace Google.Protobuf.Collections
public void Clear()
{
this.CheckMutable();
list.Clear();
map.Clear();
}
@ -233,7 +230,6 @@ namespace Google.Protobuf.Collections
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
this.CheckMutable();
if (item.Key == null)
{
throw new ArgumentException("Key is null", "item");
@ -260,31 +256,6 @@ namespace Google.Protobuf.Collections
public int Count { get { return list.Count; } }
public bool IsReadOnly { get { return frozen; } }
public void Freeze()
{
if (IsFrozen)
{
return;
}
frozen = true;
// Only values can be frozen, as all the key types are simple.
// Everything can be done in-place, as we're just freezing objects.
if (typeof(IFreezable).IsAssignableFrom(typeof(TValue)))
{
for (var node = list.First; node != null; node = node.Next)
{
var pair = node.Value;
IFreezable freezableValue = pair.Value as IFreezable;
if (freezableValue != null)
{
freezableValue.Freeze();
}
}
}
}
public bool IsFrozen { get { return frozen; } }
public override bool Equals(object other)
{
return Equals(other as MapField<TKey, TValue>);
@ -405,7 +376,6 @@ namespace Google.Protobuf.Collections
void IDictionary.Remove(object key)
{
ThrowHelper.ThrowIfNull(key, "key");
this.CheckMutable();
if (!(key is TKey))
{
return;
@ -420,7 +390,7 @@ namespace Google.Protobuf.Collections
temp.CopyTo(array, index);
}
bool IDictionary.IsFixedSize { get { return IsFrozen; } }
bool IDictionary.IsFixedSize { get { return false; } }
ICollection IDictionary.Keys { get { return (ICollection)Keys; } }

@ -43,7 +43,7 @@ namespace Google.Protobuf.Collections
/// restrictions (no null values) and capabilities (deep cloning and freezing).
/// </summary>
/// <typeparam name="T">The element type of the repeated field.</typeparam>
public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IFreezable
public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>
{
private static readonly T[] EmptyArray = new T[0];
private const int MinArraySize = 8;
@ -190,21 +190,6 @@ namespace Google.Protobuf.Collections
}
}
public bool IsFrozen { get { return frozen; } }
public void Freeze()
{
frozen = true;
IFreezable[] freezableArray = array as IFreezable[];
if (freezableArray != null)
{
for (int i = 0; i < count; i++)
{
freezableArray[i].Freeze();
}
}
}
private void EnsureSize(int size)
{
if (array.Length < size)
@ -223,14 +208,12 @@ namespace Google.Protobuf.Collections
{
throw new ArgumentNullException("item");
}
this.CheckMutable();
EnsureSize(count + 1);
array[count++] = item;
}
public void Clear()
{
this.CheckMutable();
array = EmptyArray;
count = 0;
}
@ -247,7 +230,6 @@ namespace Google.Protobuf.Collections
public bool Remove(T item)
{
this.CheckMutable();
int index = IndexOf(item);
if (index == -1)
{
@ -261,7 +243,7 @@ namespace Google.Protobuf.Collections
public int Count { get { return count; } }
public bool IsReadOnly { get { return IsFrozen; } }
public bool IsReadOnly { get { return false; } }
public void Add(RepeatedField<T> values)
{
@ -269,7 +251,6 @@ namespace Google.Protobuf.Collections
{
throw new ArgumentNullException("values");
}
this.CheckMutable();
EnsureSize(count + values.count);
// We know that all the values will be valid, because it's a RepeatedField.
Array.Copy(values.array, 0, array, count, values.count);
@ -282,7 +263,6 @@ namespace Google.Protobuf.Collections
{
throw new ArgumentNullException("values");
}
this.CheckMutable();
// TODO: Check for ICollection and get the Count?
foreach (T item in values)
{
@ -372,7 +352,6 @@ namespace Google.Protobuf.Collections
{
throw new ArgumentOutOfRangeException("index");
}
this.CheckMutable();
EnsureSize(count + 1);
Array.Copy(array, index, array, index + 1, count - index);
array[index] = item;
@ -385,7 +364,6 @@ namespace Google.Protobuf.Collections
{
throw new ArgumentOutOfRangeException("index");
}
this.CheckMutable();
Array.Copy(array, index + 1, array, index, count - index - 1);
count--;
array[count] = default(T);
@ -407,7 +385,6 @@ namespace Google.Protobuf.Collections
{
throw new ArgumentOutOfRangeException("index");
}
this.CheckMutable();
if (value == null)
{
throw new ArgumentNullException("value");
@ -417,7 +394,7 @@ namespace Google.Protobuf.Collections
}
#region Explicit interface implementation for IList and ICollection.
bool IList.IsFixedSize { get { return IsFrozen; } }
bool IList.IsFixedSize { get { return false; } }
void ICollection.CopyTo(Array array, int index)
{

@ -1,58 +0,0 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
namespace Google.Protobuf
{
/// <summary>
/// Extension methods for <see cref="IFreezable"/> types.
/// </summary>
public static class Freezable
{
/// <summary>
/// Throws an <see cref="InvalidOperationException"/> if <paramref name="target"/>
/// is frozen.
/// </summary>
/// <remarks>
/// This is a convenience methods that freezable types can call before all
/// mutations, to protect frozen objects.
/// </remarks>
public static void CheckMutable(this IFreezable target)
{
if (target.IsFrozen)
{
throw new InvalidOperationException("Attempt to mutate frozen object");
}
}
}
}

@ -64,7 +64,6 @@
<Compile Include="Compatibility\TypeExtensions.cs" />
<Compile Include="FieldCodec.cs" />
<Compile Include="FrameworkPortability.cs" />
<Compile Include="Freezable.cs" />
<Compile Include="JsonFormatter.cs" />
<Compile Include="MessageExtensions.cs" />
<Compile Include="IMessage.cs" />

@ -79,7 +79,7 @@ namespace Google.Protobuf
/// the implementation class.
/// </summary>
/// <typeparam name="T">The message type.</typeparam>
public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T>, IFreezable where T : IMessage<T>
public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T> where T : IMessage<T>
{
/// <summary>
/// Merges the given message into this one.
@ -97,10 +97,6 @@ namespace Google.Protobuf
/// Additionally, due to the type constraint on <c>T</c> in <see cref="IMessage{T}"/>,
/// it is simpler to keep this as a separate interface.
/// </para>
/// <para>
/// Freezable types which implement this interface should always return a mutable clone,
/// even if the original object is frozen.
/// </para>
/// </remarks>
/// <typeparam name="T">The type itself, returned by the <see cref="Clone"/> method.</typeparam>
public interface IDeepCloneable<T>
@ -111,32 +107,4 @@ namespace Google.Protobuf
/// <returns>A deep clone of this object.</returns>
T Clone();
}
/// <summary>
/// Provides a mechanism for freezing a message (or repeated field collection)
/// to make it immutable.
/// </summary>
/// <remarks>
/// Implementations are under no obligation to make this thread-safe: if a freezable
/// type instance is shared between threads before being frozen, and one thread then
/// freezes it, it is possible for other threads to make changes during the freezing
/// operation and also to observe stale values for mutated fields. Objects should be
/// frozen before being made available to other threads.
/// </remarks>
public interface IFreezable
{
/// <summary>
/// Freezes this object.
/// </summary>
/// <remarks>
/// If the object is already frozen, this method has no effect.
/// </remarks>
void Freeze();
/// <summary>
/// Returns whether or not this object is frozen (and therefore immutable).
/// </summary>
/// <value><c>true</c> if this object is frozen; <c>false</c> otherwise.</value>
bool IsFrozen { get; }
}
}

@ -129,8 +129,6 @@ void MapFieldGenerator::GenerateCloningCode(io::Printer* printer) {
}
void MapFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_.Freeze();\n");
}
} // namespace csharp

@ -190,7 +190,6 @@ void MessageGenerator::Generate(io::Printer* printer) {
" get { return $name$Case_; }\n"
"}\n\n"
"public void Clear$property_name$() {\n"
" pb::Freezable.CheckMutable(this);\n"
" $name$Case_ = $property_name$OneofCase.None;\n"
" $name$_ = null;\n"
"}\n\n");
@ -293,33 +292,6 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
}
void MessageGenerator::GenerateFreezingCode(io::Printer* printer) {
map<string, string> vars;
vars["class_name"] = class_name();
printer->Print(
"public void Freeze() {\n"
" if (IsFrozen) {\n"
" return;\n"
" }\n"
" _frozen = true;\n");
printer->Indent();
// Freeze non-oneof fields first (only messages and repeated fields will actually generate any code)
for (int i = 0; i < descriptor_->field_count(); i++) {
if (!descriptor_->field(i)->containing_oneof()) {
scoped_ptr<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(descriptor_->field(i)));
generator->GenerateFreezingCode(printer);
}
}
// For each oneof, if the value is freezable, freeze it. We don't actually need to know which type it was.
for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) {
vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false);
printer->Print(vars,
"if ($name$_ is IFreezable) ((IFreezable) $name$_).Freeze();\n");
}
printer->Outdent();
printer->Print("}\n\n");
}
void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {

@ -67,7 +67,6 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n"
" $name$_ = value;\n"
" }\n"
"}\n");
@ -134,8 +133,6 @@ void MessageFieldGenerator::GenerateCloningCode(io::Printer* printer) {
}
void MessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
printer->Print(variables_,
"if ($has_property_check$) $property_name$.Freeze();\n");
}
void MessageFieldGenerator::GenerateCodecCode(io::Printer* printer) {
@ -161,7 +158,6 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n"
" $oneof_name$_ = value;\n"
" $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
" }\n"

@ -73,8 +73,7 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
variables_,
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n");
" set {\n");
if (is_value_type) {
printer->Print(
variables_,
@ -176,8 +175,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
variables_,
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n");
" set {\n");
if (is_value_type) {
printer->Print(
variables_,

@ -117,8 +117,6 @@ void RepeatedEnumFieldGenerator::GenerateCloningCode(io::Printer* printer) {
}
void RepeatedEnumFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_.Freeze();\n");
}
} // namespace csharp

@ -132,8 +132,6 @@ void RepeatedMessageFieldGenerator::GenerateCloningCode(io::Printer* printer) {
}
void RepeatedMessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_.Freeze();\n");
}
} // namespace csharp

@ -115,8 +115,6 @@ void RepeatedPrimitiveFieldGenerator::GenerateCloningCode(io::Printer* printer)
}
void RepeatedPrimitiveFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_.Freeze();\n");
}
} // namespace csharp

@ -76,7 +76,6 @@ void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) {
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n"
" $name$_ = value;\n"
" }\n"
"}\n");
@ -172,7 +171,6 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n"
" $oneof_name$_ = value;\n"
" $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
" }\n"

Loading…
Cancel
Save